diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/DetailsActivity.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/DetailsActivity.kt index 00bd22a..5b4f5c0 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/DetailsActivity.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/DetailsActivity.kt @@ -32,6 +32,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.LaunchedEffect +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import com.mateusrodcosta.apps.share2storage.model.UriData @@ -42,19 +43,19 @@ import com.mateusrodcosta.apps.share2storage.utils.SharedPreferenceKeys import com.mateusrodcosta.apps.share2storage.utils.getUriData import com.mateusrodcosta.apps.share2storage.utils.saveFile import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlin.time.DurationUnit -import kotlin.time.toDuration class DetailsActivity : ComponentActivity() { private lateinit var createFile: ActivityResultLauncher - private var uriData: UriData? = null + private lateinit var fileUri: Uri + private lateinit var uriData: UriData private var skipFileDetails: Boolean = false private var defaultSaveLocation: Uri? = null private var showFilePreview: Boolean = true + private var shouldSkipFilePicker: Boolean = false + @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -64,7 +65,17 @@ class DetailsActivity : ComponentActivity() { getPreferences() handleIntent(intent) val launchFilePicker = { - createFile.launch(uriData?.displayName ?: "") + if (shouldSkipFilePicker) { + lifecycleScope.launch { + val dir = DocumentFile.fromTreeUri(applicationContext, defaultSaveLocation!!) + val file = dir!!.createFile(uriData.type, uriData.displayName) + + if (file?.uri != null) handleFileSave(file.uri, fileUri) + } + } else { + createFile.launch(uriData.displayName) + } + Unit } setContent { @@ -88,20 +99,30 @@ class DetailsActivity : ComponentActivity() { private fun getPreferences() { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) - skipFileDetails = + val skipFileDetails = sharedPreferences.getBoolean(SharedPreferenceKeys.SKIP_FILE_DETAILS_KEY, false) Log.d("details] skipFileDetails", skipFileDetails.toString()) + this.skipFileDetails = skipFileDetails val defaultSaveLocationRaw = sharedPreferences.getString(SharedPreferenceKeys.DEFAULT_SAVE_LOCATION_KEY, null) Log.d("details] defaultSaveLocationRaw", defaultSaveLocationRaw.toString()) - defaultSaveLocation = if (defaultSaveLocationRaw != null) Uri.parse(defaultSaveLocationRaw) - else null + val defaultSaveLocation = + if (defaultSaveLocationRaw != null) Uri.parse(defaultSaveLocationRaw) + else null Log.d("details] defaultSaveLocation", defaultSaveLocation.toString()) + this.defaultSaveLocation = defaultSaveLocation - showFilePreview = + val showFilePreview = sharedPreferences.getBoolean(SharedPreferenceKeys.SHOW_FILE_PREVIEW_KEY, true) Log.d("details] showFilePreview", showFilePreview.toString()) + this.showFilePreview = showFilePreview + + val skipFilePicker = + sharedPreferences.getBoolean(SharedPreferenceKeys.SKIP_FILE_PICKER, false) + Log.d("details] skipFilePicker", skipFilePicker.toString()) + // Only skip file picker if both a default folder is set and "Skip File Picker is selected" + this.shouldSkipFilePicker = defaultSaveLocation != null && skipFilePicker } private fun handleIntent(intent: Intent?) { @@ -115,10 +136,13 @@ class DetailsActivity : ComponentActivity() { Log.d("fileUri", "Action: ${intent?.action}, uri: $fileUri") if (fileUri == null) return - uriData = getUriData(contentResolver, fileUri, getPreview = showFilePreview) - if(uriData == null) return + this.fileUri = fileUri + val uriData = getUriData(contentResolver, fileUri, getPreview = showFilePreview) + if (uriData == null) return + this.uriData = uriData + createFile = registerForActivityResult( - CreateDocumentWithInitialUri(uriData?.type ?: "*/*", defaultSaveLocation) + CreateDocumentWithInitialUri(uriData.type, defaultSaveLocation) ) { uri -> if (uri == null) { if (skipFileDetails) finish() @@ -144,7 +168,7 @@ class DetailsActivity : ComponentActivity() { ).show() } - if (skipFileDetails) { + if (skipFileDetails || shouldSkipFilePicker) { finish() } } 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 8fcbf84..55b43b4 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 @@ -41,6 +41,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavController @@ -48,6 +49,7 @@ import com.mateusrodcosta.apps.share2storage.R import com.mateusrodcosta.apps.share2storage.screens.shared.AppBasicDivider import com.mateusrodcosta.apps.share2storage.screens.shared.AppListHeader import com.mateusrodcosta.apps.share2storage.ui.theme.AppTheme +import com.mateusrodcosta.apps.share2storage.utils.Utils import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -55,12 +57,14 @@ import kotlinx.coroutines.flow.StateFlow @Composable fun SettingsScreenPreview() { val mockDefaultSaveLocation = MutableStateFlow(null) + val mockSkipFilePicker = MutableStateFlow(false) val mockSkipFileDetails = MutableStateFlow(false) val mockInterceptActionViewIntents = MutableStateFlow(false) val mockShowFilePreview = MutableStateFlow(true) SettingsScreenContent( spDefaultSaveLocation = mockDefaultSaveLocation, + spSkipFilePicker = mockSkipFilePicker, spSkipFileDetails = mockSkipFileDetails, spInterceptActionViewIntents = mockInterceptActionViewIntents, spShowFilePreview = mockShowFilePreview, @@ -72,12 +76,14 @@ fun SettingsScreenPreview() { @Composable fun SettingsScreenPreviewPtBr() { val mockDefaultSaveLocation = MutableStateFlow(null) + val mockSkipFilePicker = MutableStateFlow(false) val mockSkipFileDetails = MutableStateFlow(false) val mockInterceptActionViewIntents = MutableStateFlow(false) val mockShowFilePreview = MutableStateFlow(true) SettingsScreenContent( spDefaultSaveLocation = mockDefaultSaveLocation, + spSkipFilePicker = mockSkipFilePicker, spSkipFileDetails = mockSkipFileDetails, spInterceptActionViewIntents = mockInterceptActionViewIntents, spShowFilePreview = mockShowFilePreview, @@ -89,11 +95,15 @@ fun SettingsScreen(navController: NavController, settingsViewModel: SettingsView SettingsScreenContent( navController = navController, spDefaultSaveLocation = settingsViewModel.defaultSaveLocation, + spSkipFilePicker = settingsViewModel.skipFilePicker, spSkipFileDetails = settingsViewModel.skipFileDetails, spInterceptActionViewIntents = settingsViewModel.interceptActionViewIntents, spShowFilePreview = settingsViewModel.showFilePreview, launchFilePicker = { settingsViewModel.getSaveLocationDirIntent().launch(null) }, clearSaveDirectory = { settingsViewModel.clearSaveDirectory() }, + updateSkipFilePicker = { value: Boolean -> + settingsViewModel.updateSkipFilePicker(value) + }, updateSkipFileDetails = { value: Boolean -> settingsViewModel.updateSkipFileDetails(value) }, @@ -111,11 +121,13 @@ fun SettingsScreen(navController: NavController, settingsViewModel: SettingsView fun SettingsScreenContent( navController: NavController? = null, spDefaultSaveLocation: StateFlow, + spSkipFilePicker: StateFlow, spSkipFileDetails: StateFlow, spInterceptActionViewIntents: StateFlow, spShowFilePreview: StateFlow, launchFilePicker: () -> Unit = {}, clearSaveDirectory: () -> Unit = {}, + updateSkipFilePicker: (Boolean) -> Unit = {}, updateSkipFileDetails: (Boolean) -> Unit = {}, updateInterceptActionViewIntents: (Boolean) -> Unit = {}, updateShowFilePreview: (Boolean) -> Unit = {}, @@ -145,6 +157,11 @@ fun SettingsScreenContent( clearSaveDirectory = clearSaveDirectory, spDefaultSaveLocation = spDefaultSaveLocation, ) + SkipFilePickerSetting( + spDefaultSaveLocation = spDefaultSaveLocation, + updateSkipFilePicker = updateSkipFilePicker, + spSkipFilePicker = spSkipFilePicker, + ) AppBasicDivider() AppListHeader(stringResource(R.string.settings_category_file_details)) SkipFileDetailsSetting( @@ -190,6 +207,34 @@ fun DefaultSaveLocationSetting( }) } +@Composable +fun SkipFilePickerSetting( + spDefaultSaveLocation: StateFlow, + updateSkipFilePicker: (Boolean) -> Unit, + spSkipFilePicker: StateFlow, +) { + val skipFilePicker by spSkipFilePicker.collectAsState() + val defaultSaveLocation by spDefaultSaveLocation.collectAsState() + + ListItem(modifier = if (defaultSaveLocation != null) Modifier.clickable { + updateSkipFilePicker( + !skipFilePicker + ) + } else Modifier.alpha(Utils.CONTENT_ALPHA_DISABLED), headlineContent = { + Text(stringResource(R.string.settings_skip_file_picker)) + }, supportingContent = { + Text(stringResource(R.string.settings_skip_file_picker_info)) + }, trailingContent = { + Switch( + enabled = defaultSaveLocation != null, + checked = skipFilePicker, + onCheckedChange = { value -> + updateSkipFilePicker(value) + }, + ) + }) +} + @Composable fun SkipFileDetailsSetting( updateSkipFileDetails: (Boolean) -> Unit, diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsViewModel.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsViewModel.kt index 198d688..dbac487 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/screens/SettingsViewModel.kt @@ -56,6 +56,9 @@ class SettingsViewModel : ViewModel() { private val _interceptActionViewIntents = MutableStateFlow(false) val interceptActionViewIntents: StateFlow = _interceptActionViewIntents + private val _skipFilePicker = MutableStateFlow(false) + val skipFilePicker: StateFlow = _skipFilePicker + fun assignSaveLocationDirIntent(intent: ActivityResultLauncher) { getSaveLocationDirIntent = intent } @@ -105,10 +108,15 @@ class SettingsViewModel : ViewModel() { "settings] initSharedPreferences] showFilePreview", spShowFilePreview.toString() ) + val spSkipFilePicker = + sharedPreferences.getBoolean(SharedPreferenceKeys.SKIP_FILE_PICKER, false) + Log.d("settings] initSharedPreferences] skipFilePicker", spSkipFilePicker.toString()) + _defaultSaveLocation.value = spDefaultSaveLocation _skipFileDetails.value = spSkipFileDetails _interceptActionViewIntents.value = spInterceptActionViewIntents _showFilePreview.value = spShowFilePreview + _skipFilePicker.value = spSkipFilePicker } fun updateDefaultSaveLocation(value: Uri?) { @@ -194,4 +202,12 @@ class SettingsViewModel : ViewModel() { _showFilePreview.value = value } } + + fun updateSkipFilePicker(value: Boolean) { + sharedPreferences.edit(commit = true) { + putBoolean(SharedPreferenceKeys.SKIP_FILE_PICKER, value) + Log.e("settings] updateSkipFilePicker", value.toString()) + _skipFilePicker.value = value + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/SharedPreferenceKeys.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/SharedPreferenceKeys.kt index 938ad2c..7886ba2 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/SharedPreferenceKeys.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/SharedPreferenceKeys.kt @@ -22,4 +22,5 @@ object SharedPreferenceKeys { const val DEFAULT_SAVE_LOCATION_KEY: String = "default_save_location" const val INTERCEPT_ACTION_VIEW_INTENTS_KEY: String = "intercept_action_view_intents" const val SHOW_FILE_PREVIEW_KEY: String = "show_file_preview" + const val SKIP_FILE_PICKER: String = "skip_file_picker" } \ No newline at end of file diff --git a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/Utils.kt b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/Utils.kt index 333ab83..29bc78b 100644 --- a/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/Utils.kt +++ b/app/src/main/kotlin/com/mateusrodcosta/apps/share2storage/utils/Utils.kt @@ -31,6 +31,7 @@ import java.io.* object Utils { const val BUFFER_SIZE: Int = 1024 + const val CONTENT_ALPHA_DISABLED = 0.38f } fun getUriData(contentResolver: ContentResolver, uri: Uri, getPreview: Boolean): UriData? { diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a3096e1..88f06a5 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -22,6 +22,8 @@ Seletor de Arquivos Local de salvamento padrão Última pasta usada + Pular seletor de arquivos + Pular seletor de arquivos e salvar imediatamente (apenas disponível quando uma pasta padrão for configurada) Detalhes do arquivo Pular página de Detalhes do arquivo @@ -34,7 +36,7 @@ Permite registrar como uma opção quando aplicativos tentam automaticamente abrir um visualizador de arquivos Nenhum arquivo - Salvando arquivo... + Salvando arquivo… Detalhes do arquivo Nome do arquivo diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 443b148..bd28d8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,6 +22,8 @@ File Picker Default save location Last used folder + Skip File Picker + Skip File Picker and immediately save files (only available when a default folder is set) File Details Skip File Details page @@ -34,7 +36,7 @@ Allow registering as an option when apps try to automatically open a file viewer No file found - Saving file... + Saving file… File Details File name