diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3abe2b6..48e55b4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,10 +19,6 @@ - - diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/MainActivity.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/MainActivity.kt index 355c154..535cd86 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/MainActivity.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/MainActivity.kt @@ -18,16 +18,36 @@ package com.mateusrodcosta.apps.share2storage import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import com.mateusrodcosta.apps.share2storage.screens.MainScreen +import androidx.activity.result.contract.ActivityResultContracts +import com.mateusrodcosta.apps.share2storage.screens.AppNavigation class MainActivity : ComponentActivity() { + + private val settingsViewModel: SettingsViewModel = SettingsViewModel() + + private val getSaveLocationDirIntent = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + if (uri == null) return@registerForActivityResult + + Log.d("settings] getSaveLocationDir] uri", uri.toString()) + Log.d("settings] getSaveLocationDir] uri.path", uri.path.toString()) + + settingsViewModel.updateDefaultSaveLocation(uri) + } + override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) - setContent { MainScreen() } + + settingsViewModel.receiveContext(applicationContext) + settingsViewModel.assignSaveLocationDirIntent(getSaveLocationDirIntent) + settingsViewModel.initPreferences() + + setContent { AppNavigation(settingsViewModel) } } } diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsActivity.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsActivity.kt deleted file mode 100644 index 0b23b9a..0000000 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsActivity.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2022 - 2024 Mateus Rodrigues Costa - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.mateusrodcosta.apps.share2storage - -import android.os.Bundle -import android.util.Log -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.activity.result.contract.ActivityResultContracts -import com.mateusrodcosta.apps.share2storage.screens.SettingsScreen - -class SettingsActivity : ComponentActivity() { - - private val settingsViewModel: SettingsViewModel = SettingsViewModel() - - private val getSaveLocationDirIntent = - registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> - if (uri == null) return@registerForActivityResult - - Log.d("settings] getSaveLocationDir] uri", uri.toString()) - Log.d("settings] getSaveLocationDir] uri.path", uri.path.toString()) - - settingsViewModel.updateDefaultSaveLocation(uri) - } - - override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() - super.onCreate(savedInstanceState) - - settingsViewModel.receiveContext(applicationContext) - settingsViewModel.initPreferences() - - setContent { - SettingsScreen( - spDefaultSaveLocation = settingsViewModel.defaultSaveLocation, - spSkipFileDetails = settingsViewModel.skipFileDetails, - launchFilePicker = { getSaveLocationDirIntent.launch(null) }, - clearSaveDirectory = { settingsViewModel.clearSaveDirectory() }, - updateSkipFileDetails = { value: Boolean -> - settingsViewModel.updateSkipFileDetails(value) - }, - closeActivity = { this.finish() } - ) - } - } -} diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsViewModel.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsViewModel.kt index a316c5f..0ea1024 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/SettingsViewModel.kt @@ -23,6 +23,7 @@ import android.content.Intent import android.content.SharedPreferences import android.net.Uri import android.util.Log +import androidx.activity.result.ActivityResultLauncher import androidx.core.content.edit import androidx.lifecycle.ViewModel import androidx.preference.PreferenceManager @@ -34,6 +35,7 @@ class SettingsViewModel : ViewModel() { private lateinit var sharedPreferences: SharedPreferences private lateinit var contentResolver: ContentResolver + private lateinit var getSaveLocationDirIntent: ActivityResultLauncher private val _defaultSaveLocation = MutableStateFlow(null) val defaultSaveLocation: StateFlow = _defaultSaveLocation @@ -41,6 +43,14 @@ class SettingsViewModel : ViewModel() { private val _skipFileDetails = MutableStateFlow(false) val skipFileDetails: StateFlow = _skipFileDetails + fun assignSaveLocationDirIntent(intent: ActivityResultLauncher) { + getSaveLocationDirIntent = intent + } + + fun getSaveLocationDirIntent(): ActivityResultLauncher { + return getSaveLocationDirIntent + } + fun receiveContext(context: Context) { sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) contentResolver = context.contentResolver diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/AppNavigation.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/AppNavigation.kt new file mode 100644 index 0000000..c9f33af --- /dev/null +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/AppNavigation.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 - 2024 Mateus Rodrigues Costa + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.mateusrodcosta.apps.share2storage.screens + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.mateusrodcosta.apps.share2storage.SettingsViewModel + +@Composable +fun AppNavigation(settingsViewModel: SettingsViewModel) { + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = "main") { + composable("main") { MainScreen(navController) } + composable("settings") { SettingsScreen(navController, settingsViewModel) } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/MainScreen.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/MainScreen.kt index f8862a4..574700e 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/MainScreen.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/MainScreen.kt @@ -17,7 +17,6 @@ package com.mateusrodcosta.apps.share2storage.screens -import android.content.Intent import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -46,15 +45,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import com.mateusrodcosta.apps.share2storage.R -import com.mateusrodcosta.apps.share2storage.SettingsActivity import com.mateusrodcosta.apps.share2storage.ui.theme.AppTheme import com.mateusrodcosta.apps.share2storage.utils.AppBasicDivider import com.mateusrodcosta.apps.share2storage.utils.appTopAppBarColors @@ -62,22 +60,23 @@ import com.mateusrodcosta.apps.share2storage.utils.appTopAppBarColors @Preview(apiLevel = 33, showBackground = true) @Composable fun MainScreenPreview() { - MainScreen() + MainScreenContent(navController = null) +} + +@Composable +fun MainScreen(navController: NavController) { + MainScreenContent(navController = navController) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MainScreen() { - val context = LocalContext.current +fun MainScreenContent(navController: NavController?) { AppTheme { Scaffold(topBar = { TopAppBar( title = { Text(stringResource(R.string.app_name)) }, actions = { - IconButton(onClick = { - val intent = Intent(context, SettingsActivity::class.java) - context.startActivity(intent) - }) { + IconButton(onClick = { navController?.navigate("settings") }) { Icon(Icons.Rounded.Settings, stringResource(id = R.string.settings)) } }, diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsScreen.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsScreen.kt index 6b5444b..e1666dd 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsScreen.kt @@ -49,7 +49,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import com.mateusrodcosta.apps.share2storage.R +import com.mateusrodcosta.apps.share2storage.SettingsViewModel import com.mateusrodcosta.apps.share2storage.ui.theme.AppTheme import com.mateusrodcosta.apps.share2storage.utils.AppBasicDivider import com.mateusrodcosta.apps.share2storage.utils.appTopAppBarColors @@ -62,29 +64,40 @@ fun SettingsScreenPreview() { val mockDefaultSaveLocation = MutableStateFlow(null) val mockSkipFileDetails = MutableStateFlow(false) - SettingsScreen( + SettingsScreenContent( + navController = null, spDefaultSaveLocation = mockDefaultSaveLocation, spSkipFileDetails = mockSkipFileDetails, launchFilePicker = {}, clearSaveDirectory = {}, - updateSkipFileDetails = { _ -> }, - closeActivity = {}, + updateSkipFileDetails = {}, + ) +} + +@Composable +fun SettingsScreen(navController: NavController, settingsViewModel: SettingsViewModel) { + SettingsScreenContent( + navController = navController, + spDefaultSaveLocation = settingsViewModel.defaultSaveLocation, + spSkipFileDetails = settingsViewModel.skipFileDetails, + launchFilePicker = { settingsViewModel.getSaveLocationDirIntent().launch(null) }, + clearSaveDirectory = { settingsViewModel.clearSaveDirectory() }, + updateSkipFileDetails = { value: Boolean -> + settingsViewModel.updateSkipFileDetails(value) + }, ) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingsScreen( +fun SettingsScreenContent( + navController: NavController?, spDefaultSaveLocation: StateFlow, spSkipFileDetails: StateFlow, - launchFilePicker: (() -> Unit), - clearSaveDirectory: (() -> Unit), - updateSkipFileDetails: ((Boolean) -> Unit), - closeActivity: () -> Unit, + launchFilePicker: () -> Unit, + clearSaveDirectory: () -> Unit, + updateSkipFileDetails: (Boolean) -> Unit, ) { - val defaultSaveLocation by spDefaultSaveLocation.collectAsState() - val skipFileDetails by spSkipFileDetails.collectAsState() - val settingsPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp) AppTheme { @@ -92,7 +105,7 @@ fun SettingsScreen( TopAppBar(title = { Text(stringResource(R.string.settings)) }, colors = appTopAppBarColors(), navigationIcon = { - IconButton(onClick = { closeActivity() }) { + IconButton(onClick = { navController?.navigateUp() }) { Icon( Icons.AutoMirrored.Filled.ArrowBack, stringResource(id = R.string.back_arrow) @@ -110,14 +123,14 @@ fun SettingsScreen( Column { SkipFileDetailsSetting( updateSkipFileDetails = updateSkipFileDetails, - skipFileDetails = skipFileDetails, + spSkipFileDetails = spSkipFileDetails, paddingValues = settingsPadding, ) AppBasicDivider() DefaultSaveLocationSetting( launchFilePicker = launchFilePicker, clearSaveDirectory = clearSaveDirectory, - defaultSaveLocation = defaultSaveLocation, + spDefaultSaveLocation = spDefaultSaveLocation, paddingValues = settingsPadding, ) AppBasicDivider() @@ -130,10 +143,12 @@ fun SettingsScreen( @Composable fun SkipFileDetailsSetting( - updateSkipFileDetails: ((Boolean) -> Unit), - skipFileDetails: Boolean, + updateSkipFileDetails: (Boolean) -> Unit, + spSkipFileDetails: StateFlow, paddingValues: PaddingValues, ) { + val skipFileDetails by spSkipFileDetails.collectAsState() + Row(modifier = Modifier .clickable { updateSkipFileDetails(!skipFileDetails) } .padding(paddingValues) @@ -158,11 +173,13 @@ fun SkipFileDetailsSetting( @Composable fun DefaultSaveLocationSetting( - launchFilePicker: (() -> Unit), - clearSaveDirectory: (() -> Unit), - defaultSaveLocation: Uri?, + launchFilePicker: () -> Unit, + clearSaveDirectory: () -> Unit, + spDefaultSaveLocation: StateFlow, paddingValues: PaddingValues, ) { + val defaultSaveLocation by spDefaultSaveLocation.collectAsState() + Row(modifier = Modifier .clickable { launchFilePicker() } .padding(paddingValues) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed97338..08955a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ preference-ktx = "1.2.1" composeBom = "2024.01.00" composeMaterial3WindowSizeClass = "1.1.2" activityCompose = "1.8.2" +navigationCompose = "2.7.7" junit = "4.13.2" androidxTestRunner = "1.5.2" androidxTestRules = "1.5.0" @@ -23,6 +24,7 @@ compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } junit-junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-test-runner = { group = "androidx.test", name="runner", version.ref = "androidxTestRunner" } androidx-test-rules = { group = "androidx.test", name="rules", version.ref = "androidxTestRules" } @@ -31,7 +33,7 @@ androidx-test-rules = { group = "androidx.test", name="rules", version.ref = "an androidx-ktx = ["androidx.core.ktx", "androidx.preference.ktx"] compose = ["compose.material3", "compose.material3.window.size.classes", "compose.ui.tooling.preview", "compose.ui.material.icons.extended"] compose-debug = ["compose.ui.tooling", "compose.ui.test.manifest"] -androidx-compose-integration = ["androidx.activity.compose"] +androidx-compose-integration = ["androidx.activity.compose", "androidx.navigation.compose"] testing = ["junit.junit"] ui-testing = ["androidx.test.runner", "androidx.test.rules", "compose.ui.test.junit4"]