diff --git a/.idea/misc.xml b/.idea/misc.xml index b2c751a..a4f09e2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ + - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 80d89ba..4dd8821 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) + alias(libs.plugins.kotlin.serialization) } android { @@ -54,6 +55,7 @@ dependencies { implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.knbt) implementation(libs.reorderable) + implementation(libs.androidx.navigation.compose) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ffebed1..25b3816 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,10 +1,16 @@ + + + + + + - - - - - - \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/core/Backup.kt b/app/src/main/java/eu/m724/pojavbackup/core/Backup.kt new file mode 100644 index 0000000..c7b91d9 --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/core/Backup.kt @@ -0,0 +1,10 @@ +package eu.m724.pojavbackup.core + +import java.time.Instant + +data class Backup( + val timestamp: Instant, + val status: BackupStatus +) { + +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/core/BackupStatus.kt b/app/src/main/java/eu/m724/pojavbackup/core/BackupStatus.kt new file mode 100644 index 0000000..2f19b2c --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/core/BackupStatus.kt @@ -0,0 +1,8 @@ +package eu.m724.pojavbackup.core + +enum class BackupStatus { + SUCCESS, + FAILURE, + ONGOING, + ABORTED +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/core/DataLoader.kt b/app/src/main/java/eu/m724/pojavbackup/core/DataLoader.kt new file mode 100644 index 0000000..1322d45 --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/core/DataLoader.kt @@ -0,0 +1,29 @@ +package eu.m724.pojavbackup.core + +import android.content.ContentResolver +import androidx.documentfile.provider.DocumentFile + +class DataLoader( + private val contentResolver: ContentResolver, + private val dataDirectory: DocumentFile +) { + + fun listWorlds(): List { + return WorldDetector(contentResolver, getSavesDirectory()) + .listWorlds() + } + + fun listWorlds(consumer: (World) -> Unit) { + WorldDetector(contentResolver, getSavesDirectory()) + .listWorlds(consumer) + } + + fun getWorld(id: String): World? { + return WorldDetector(contentResolver, getSavesDirectory()) + .getWorld(id) + } + + fun getSavesDirectory(): DocumentFile { + return dataDirectory.findFile(".minecraft")!!.findFile("saves")!! + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/home/HomeActivity.kt b/app/src/main/java/eu/m724/pojavbackup/home/HomeActivity.kt new file mode 100644 index 0000000..be3f3c2 --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/home/HomeActivity.kt @@ -0,0 +1,150 @@ +package eu.m724.pojavbackup.home + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.navigation.NavController +import androidx.navigation.NavDestination.Companion.hierarchy +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import eu.m724.pojavbackup.R +import eu.m724.pojavbackup.home.screen.Screen +import eu.m724.pojavbackup.home.screen.dashboard.DashboardScreen +import eu.m724.pojavbackup.home.screen.history.HistoryScreen +import eu.m724.pojavbackup.ui.theme.PojavBackupTheme + +class HomeActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + setContent { + val navController = rememberNavController() + + PojavBackupTheme { + Scaffold( + modifier = Modifier.fillMaxSize(), + bottomBar = { + NavigationBar { + ScreenNavigationBarItem( + navController = navController, + label = "Dashboard", + route = Screen.Dashboard, + iconResourceId = R.drawable.baseline_home_24 + ) + ScreenNavigationBarItem( + navController = navController, + label = "History", + route = Screen.History, + iconResourceId = R.drawable.baseline_history_24 + ) + } + } + ) { innerPadding -> + NavHost( + navController = navController, + startDestination = Screen.Dashboard, + modifier = Modifier.padding(innerPadding), + enterTransition = { + fadeIn() + slideInHorizontally(initialOffsetX = { it / 10 }) + }, + exitTransition = { + fadeOut() + slideOutHorizontally(targetOffsetX = { -it / 10 }) + } + ) { + composable { + DashboardScreen(navController) + } + composable { + HistoryScreen() + } + // Add more destinations similarly. + } + } + } + } + } +} + +@Composable +fun RowScope.ScreenNavigationBarItem( + navController: NavController, + label: String, + route: Screen, + iconResourceId: Int +) { + val selected = isSelected(navController, route) + + NavigationBarItem( + selected = selected, + onClick = { + if (!selected) { + navController.navigate(route) + } + }, + icon = { + Icon( + painter = painterResource(iconResourceId), + contentDescription = label + ) + }, + label = { + Text(label) + } + ) +} + +@Composable +fun isSelected( + navController: NavController, + route: Screen +): Boolean { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination + + // Check if the current destination's hierarchy contains a destination + // whose route definition matches the target 'route' object. + + // For @Serializable object routes, Navigation Compose typically uses a stable + // route string derived from the object's definition. We compare against that. + return currentDestination?.hierarchy?.any { destination -> + // Check if the destination in the hierarchy corresponds to the + // target 'route' object passed into this function. + // For @Serializable objects/classes used as routes, comparing + // the destination's 'route' property is the standard way. + destination.route == navController.graph.findNode(route)?.route + // Explanation: + // 1. `navController.graph.findNode(route)`: Finds the NavDestination node + // within the navigation graph that corresponds to your @Serializable 'route' object. + // (Requires NavController knows about this route, typically added via `composable(typeMap = ...)`) + // 2. `?.route`: Gets the unique route string pattern associated with that node. + // 3. `destination.route`: Gets the route string pattern of the current destination being checked in the hierarchy. + // 4. `==`: Compares if they are the same route. + + } == true // If currentDestination is null, it's not selected, return false. +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/home/HomeViewModel.kt b/app/src/main/java/eu/m724/pojavbackup/home/HomeViewModel.kt new file mode 100644 index 0000000..f405120 --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/home/HomeViewModel.kt @@ -0,0 +1,4 @@ +package eu.m724.pojavbackup.home + +class HomeViewModel { +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/home/screen/Screen.kt b/app/src/main/java/eu/m724/pojavbackup/home/screen/Screen.kt new file mode 100644 index 0000000..f72a61e --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/home/screen/Screen.kt @@ -0,0 +1,29 @@ +package eu.m724.pojavbackup.home.screen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import kotlinx.serialization.Serializable + +@Serializable sealed interface Screen { + @Serializable data object Dashboard : Screen + @Serializable data object History : Screen +} + +@Composable +fun ScreenColumn( + modifier: Modifier = Modifier, + content: @Composable (ColumnScope.() -> Unit) +) { + Column( + modifier = modifier.fillMaxSize().padding(top = 50.dp), + horizontalAlignment = Alignment.CenterHorizontally, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/home/screen/dashboard/DashboardScreen.kt b/app/src/main/java/eu/m724/pojavbackup/home/screen/dashboard/DashboardScreen.kt new file mode 100644 index 0000000..a91934b --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/home/screen/dashboard/DashboardScreen.kt @@ -0,0 +1,126 @@ +package eu.m724.pojavbackup.home.screen.dashboard + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import eu.m724.pojavbackup.R +import eu.m724.pojavbackup.home.screen.Screen +import eu.m724.pojavbackup.home.screen.ScreenColumn + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun DashboardScreen( + navController: NavController +) { + ScreenColumn { + FlowRow( + maxItemsInEachRow = 3 + ) { + DashboardCard( + title = "Worlds included", + value = "1", + iconResourceId = R.drawable.baseline_mosque_24, + onClick = { + + } + ) + + DashboardCard( + title = "Health", + value = "Good", + iconResourceId = R.drawable.baseline_heart_broken_24 + ) + + DashboardCard( + title = "Last backup", + value = "1d ago", + iconResourceId = R.drawable.baseline_access_time_filled_24, + onClick = { + navController.navigate(Screen.History) + } + ) + } + } +} + +@Composable +fun DashboardCard( + title: String, + value: Any, + iconResourceId: Int? = null, + onClick: (() -> Unit)? = null +) { + // TODO or card? or with card colors? + ElevatedCard( + modifier = Modifier.padding(5.dp).width(180.dp) + .clickable( + enabled = onClick != null, + onClick = onClick ?: {} + ) + ) { + Column( + modifier = Modifier.padding( + horizontal = 20.dp, + vertical = 10.dp + ) + ) { + Text( + text = title + ) + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + if (iconResourceId != null) { + Icon( + painter = painterResource(iconResourceId), + contentDescription = null, + modifier = Modifier.padding(start = 2.dp, end = 6.dp).size(26.dp), + tint = MaterialTheme.colorScheme.secondary + ) + } + + Text( + text = value.toString(), + fontSize = 26.sp, + fontWeight = FontWeight.Light + ) + + Spacer( + modifier = Modifier.weight(1f) + ) + + if (onClick != null) { + Icon( + painter = painterResource(R.drawable.baseline_arrow_forward_ios_24), + contentDescription = "Go to $title", + modifier = Modifier.padding(end = 2.dp).size(12.dp), + tint = MaterialTheme.colorScheme.secondary + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/home/screen/history/HistoryScreen.kt b/app/src/main/java/eu/m724/pojavbackup/home/screen/history/HistoryScreen.kt new file mode 100644 index 0000000..c8fe9d3 --- /dev/null +++ b/app/src/main/java/eu/m724/pojavbackup/home/screen/history/HistoryScreen.kt @@ -0,0 +1,138 @@ +package eu.m724.pojavbackup.home.screen.history + +import android.system.Os.stat +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CardElevation +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import eu.m724.pojavbackup.R +import eu.m724.pojavbackup.core.BackupStatus +import eu.m724.pojavbackup.home.screen.ScreenColumn +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import java.util.Locale + +@Composable +fun HistoryScreen() { + ScreenColumn { + Column { + BackupCard( + status = BackupStatus.ONGOING, + dateTime = ZonedDateTime.now().minusDays(28) + ) + BackupCard( + status = BackupStatus.SUCCESS, + dateTime = ZonedDateTime.now().minusDays(7) + ) + BackupCard( + status = BackupStatus.FAILURE, + dateTime = ZonedDateTime.now().minusDays(14) + ) + BackupCard( + status = BackupStatus.ABORTED, + dateTime = ZonedDateTime.now().minusDays(21) + ) + + } + } +} + +/** + * A Composable Card that displays a square icon (Bitmap or default drawable) on the left + * and text information (ID, Display Name, formatted Timestamp) on the right. + * + * @param modifier Optional Modifier for the Card. + * @param status Backup status + * @param dateTime The ZonedDateTime timestamp to display, formatted by locale. + */ +@Composable +fun BackupCard( + modifier: Modifier = Modifier, + status: BackupStatus, + dateTime: ZonedDateTime +) { + // Formatter for the timestamp - remember caches the formatter across recompositions + val formatter = remember { + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) // Adjust FormatStyle as needed (SHORT, MEDIUM, LONG, FULL) + } + val formattedTimestamp = remember(dateTime, formatter) { // Only reformat when timestamp or formatter changes + dateTime.format(formatter) + } + + val cardColors = when (status) { + BackupStatus.SUCCESS -> CardDefaults.cardColors() + BackupStatus.FAILURE -> CardDefaults.cardColors().copy( + containerColor = MaterialTheme.colorScheme.errorContainer + ) + BackupStatus.ONGOING -> CardDefaults.cardColors().copy( + containerColor = MaterialTheme.colorScheme.secondaryContainer + ) + BackupStatus.ABORTED -> CardDefaults.cardColors().copy( + containerColor = MaterialTheme.colorScheme.surfaceContainer + ) + } + + ElevatedCard( + modifier = modifier + .padding(horizontal = 8.dp, vertical = 4.dp) + .width(300.dp), + colors = cardColors + ) { + Row( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.baseline_history_24), + contentDescription = null, + modifier = Modifier + .size(64.dp) + .align(Alignment.CenterVertically) + .clip(CardDefaults.shape), // TODO match corner radius + contentScale = ContentScale.Crop // Crop is usually best for fixed aspect ratio + ) + + Spacer(modifier = Modifier.width(16.dp)) + + // --- Text Column --- + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = status.toString().lowercase().replaceFirstChar { it.uppercase()}, + style = MaterialTheme.typography.titleLarge + ) + Spacer( + modifier = Modifier.width(5.dp) + ) + Text( + text = "$formattedTimestamp", // Use formatted timestamp + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/setup/SetupActivity.kt b/app/src/main/java/eu/m724/pojavbackup/setup/SetupActivity.kt index aaa05f7..3eac915 100644 --- a/app/src/main/java/eu/m724/pojavbackup/setup/SetupActivity.kt +++ b/app/src/main/java/eu/m724/pojavbackup/setup/SetupActivity.kt @@ -1,7 +1,10 @@ package eu.m724.pojavbackup.setup +import android.R.attr.end +import android.content.Intent import android.graphics.Bitmap import android.os.Bundle +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -47,23 +50,41 @@ import androidx.compose.ui.unit.sp import eu.m724.pojavbackup.ui.theme.PojavBackupTheme import androidx.core.net.toUri import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.compose.NavHost import eu.m724.pojavbackup.R +import eu.m724.pojavbackup.home.HomeActivity import kotlinx.coroutines.flow.update import org.burnoutcrew.reorderable.ReorderableItem +import org.burnoutcrew.reorderable.detectReorder import org.burnoutcrew.reorderable.detectReorderAfterLongPress import org.burnoutcrew.reorderable.rememberReorderableLazyListState import org.burnoutcrew.reorderable.reorderable import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.format.FormatStyle +import kotlin.jvm.javaClass class MainActivity : ComponentActivity() { private val viewModel: SetupViewModel by viewModels() private val openDocumentTree = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { - viewModel.onOpenDocumentTree(applicationContext, it) + viewModel.onOpenDocumentTree(applicationContext, it, { success -> + if (success) { + onComplete() + } else { + // TODO instead red text? + Toast.makeText(applicationContext, "This is not a PojavLauncher directory.", Toast.LENGTH_SHORT).show() + } + }) } + fun onComplete() { + startActivity(Intent(applicationContext, HomeActivity::class.java)) + finishActivity(0) + } + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -77,6 +98,10 @@ class MainActivity : ComponentActivity() { uri ) + if (hasPermission) { + onComplete() + } + setContent { PojavBackupTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> @@ -104,7 +129,7 @@ fun SetupScreen( val worldsState by viewModel.worlds.collectAsStateWithLifecycle() Column( - modifier = modifier.fillMaxWidth(), + modifier = modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { Text( @@ -154,7 +179,7 @@ fun SetupScreen( } // TODO this is for testing world loading only - val state = rememberReorderableLazyListState(onMove = { from, to -> + /*val state = rememberReorderableLazyListState(onMove = { from, to -> viewModel.moveWorld(from.index, to.index) }) @@ -186,7 +211,7 @@ fun SetupScreen( } } } - } + }*/ } } diff --git a/app/src/main/java/eu/m724/pojavbackup/setup/SetupViewModel.kt b/app/src/main/java/eu/m724/pojavbackup/setup/SetupViewModel.kt index 8f07841..16621f4 100644 --- a/app/src/main/java/eu/m724/pojavbackup/setup/SetupViewModel.kt +++ b/app/src/main/java/eu/m724/pojavbackup/setup/SetupViewModel.kt @@ -37,7 +37,7 @@ class SetupViewModel : ViewModel() { val worlds: StateFlow> = _worlds.asStateFlow() // TODO we could make the check call separate and not pass context here - fun onOpenDocumentTree(context: Context, uri: Uri?) { + fun onOpenDocumentTree(context: Context, uri: Uri?, result: (Boolean) -> Unit) { if (uri != null) { Log.i(TAG, "Got URI: $uri") @@ -47,6 +47,7 @@ class SetupViewModel : ViewModel() { ) val hasPermission = checkForStoragePermission(context, uri) + result(hasPermission) } } diff --git a/app/src/main/res/drawable/baseline_access_time_filled_24.xml b/app/src/main/res/drawable/baseline_access_time_filled_24.xml new file mode 100644 index 0000000..759b617 --- /dev/null +++ b/app/src/main/res/drawable/baseline_access_time_filled_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_arrow_forward_ios_24.xml b/app/src/main/res/drawable/baseline_arrow_forward_ios_24.xml new file mode 100644 index 0000000..19201cb --- /dev/null +++ b/app/src/main/res/drawable/baseline_arrow_forward_ios_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_heart_broken_24.xml b/app/src/main/res/drawable/baseline_heart_broken_24.xml new file mode 100644 index 0000000..1d3e857 --- /dev/null +++ b/app/src/main/res/drawable/baseline_heart_broken_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_history_24.xml b/app/src/main/res/drawable/baseline_history_24.xml new file mode 100644 index 0000000..6ad75a1 --- /dev/null +++ b/app/src/main/res/drawable/baseline_history_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_home_24.xml b/app/src/main/res/drawable/baseline_home_24.xml new file mode 100644 index 0000000..20cb4d6 --- /dev/null +++ b/app/src/main/res/drawable/baseline_home_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_mosque_24.xml b/app/src/main/res/drawable/baseline_mosque_24.xml new file mode 100644 index 0000000..15f517d --- /dev/null +++ b/app/src/main/res/drawable/baseline_mosque_24.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4925864..f6986a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ PojavBackup MainActivity + HomeActivity \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 952b930..5ba8ae0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,4 +3,5 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.kotlin.serialization) apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8ee04d0..7c8d803 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,19 @@ [versions] agp = "8.9.1" kotlin = "2.0.21" -coreKtx = "1.10.1" +coreKtx = "1.16.0" junit = "4.13.2" -junitVersion = "1.1.5" -espressoCore = "3.5.1" -appcompat = "1.6.1" -material = "1.10.0" -lifecycleRuntimeKtx = "2.6.1" -activityCompose = "1.8.0" -composeBom = "2024.09.00" +junitVersion = "1.2.1" +espressoCore = "3.6.1" +appcompat = "1.7.0" +material = "1.12.0" +lifecycleRuntimeKtx = "2.8.7" +activityCompose = "1.10.1" +composeBom = "2025.04.00" lifecycleViewmodelCompose = "2.8.7" knbt = "0.11.8" reorderable = "0.9.6" +navigation = "2.8.9" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -34,9 +35,11 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } knbt = { group = "net.benwoodworth.knbt", name = "knbt", version.ref = "knbt" } reorderable = { group = "org.burnoutcrew.composereorderable", name = "reorderable", version.ref = "reorderable" } +androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}