diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee6e7fd2bc80..20c10607b707 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -358,7 +358,7 @@ android:exported="false" android:launchMode="singleInstance" /> diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index dc7194f78b0a..954fb16f8c3b 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -99,6 +99,7 @@ import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; import com.owncloud.android.ui.dialog.TermsOfServiceDialog; +import com.owncloud.android.ui.dialog.ThemeSelectionDialog; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FeatureFragment; @@ -423,6 +424,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract ChooseStorageLocationDialogFragment chooseStorageLocationDialogFragment(); + @ContributesAndroidInjector + abstract ThemeSelectionDialog themeSelectionDialog(); + @ContributesAndroidInjector abstract SharePasswordDialogFragment sharePasswordDialogFragment(); diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt index fc9760fd92b9..5b95203c6900 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -25,6 +25,7 @@ import com.owncloud.android.datastorage.DataStorageProvider import com.owncloud.android.datastorage.StoragePoint import com.owncloud.android.datastorage.StoragePoint.PrivacyType import com.owncloud.android.datastorage.StoragePoint.StorageType +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File @@ -151,19 +152,13 @@ class ChooseStorageLocationDialogFragment : ?: return val resultBundle = Bundle().apply { - putString(KEY_RESULT_STORAGE_LOCATION, newPath.path) + putString(ExtendedSettingsActivityDialog.StorageLocation.key, newPath.path) } - parentFragmentManager.setFragmentResult(KEY_RESULT_STORAGE_LOCATION, resultBundle) + parentFragmentManager.setFragmentResult(ExtendedSettingsActivityDialog.StorageLocation.key, resultBundle) } companion object { - const val KEY_RESULT_STORAGE_LOCATION = "KEY_RESULT_STORAGE_LOCATION" - const val STORAGE_LOCATION_RESULT_CODE = 100 - - @JvmStatic - fun newInstance() = ChooseStorageLocationDialogFragment() - @JvmStatic val TAG: String = Companion::class.java.simpleName } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt deleted file mode 100644 index 8d13d53e7ddf..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.ui.activity - -import android.content.Intent -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import com.nextcloud.ui.ChooseStorageLocationDialogFragment - -class ChooseStorageLocationActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val chooseStorageLocationDialogFragment = ChooseStorageLocationDialogFragment.newInstance() - supportFragmentManager.setFragmentResultListener( - KEY_RESULT_STORAGE_LOCATION, - this - ) { _, result -> - setResult( - ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, - Intent().putExtra( - KEY_RESULT_STORAGE_LOCATION, - result.getString(KEY_RESULT_STORAGE_LOCATION) - ) - ) - } - chooseStorageLocationDialogFragment.show(supportFragmentManager, "choose_storage_location") - } - - companion object { - const val KEY_RESULT_STORAGE_LOCATION = ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt new file mode 100644 index 000000000000..d96c91f2c0c8 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt @@ -0,0 +1,60 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.activity + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog + +class ExtendedSettingsActivity : AppCompatActivity() { + + private var dialogShown = false + + @Suppress("ReturnCount") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dialogShown = savedInstanceState.getBoolean(KEY_DIALOG_SHOWN, false) + } + + if (dialogShown) { + return + } + + val dialogKey = intent.getStringExtra(EXTRA_DIALOG_TYPE) ?: run { + finish() + return + } + + val dialogType = ExtendedSettingsActivityDialog.entries.find { it.key == dialogKey } ?: run { + finish() + return + } + + dialogType.showDialog(this) + dialogShown = true + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(KEY_DIALOG_SHOWN, dialogShown) + } + + companion object { + private const val EXTRA_DIALOG_TYPE = "dialog_type" + private const val KEY_DIALOG_SHOWN = "dialog_shown" + + fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent = + Intent(context, ExtendedSettingsActivity::class.java).apply { + putExtra(EXTRA_DIALOG_TYPE, dialogType.key) + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ba6db7333a81..65e70c062977 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -70,6 +70,7 @@ import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog; import com.owncloud.android.utils.ClipboardUtil; import com.owncloud.android.utils.DeviceCredentialUtils; import com.owncloud.android.utils.DisplayUtils; @@ -80,7 +81,6 @@ import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import javax.inject.Inject; @@ -121,7 +121,6 @@ public class SettingsActivity extends PreferenceActivity private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10; private static final int ACTION_SHOW_MNEMONIC = 11; private static final int ACTION_E2E = 12; - private static final int ACTION_SET_STORAGE_LOCATION = 13; private static final int TRUE_VALUE = 1; private static final String DAV_PATH = "/remote.php/dav"; @@ -879,41 +878,42 @@ private void setupGeneralCategory() { prefDataLoc = findPreference(AppPreferencesImpl.DATA_STORAGE_LOCATION); if (prefDataLoc != null) { prefDataLoc.setOnPreferenceClickListener(p -> { - Intent intent = new Intent(MainApp.getAppContext(), ChooseStorageLocationActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - startActivityForResult(intent, ACTION_SET_STORAGE_LOCATION); + Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.StorageLocation); + startActivityForResult(intent, ExtendedSettingsActivityDialog.StorageLocation.getResultId()); return true; }); } - ListPreference themePref = (ListPreference) findPreference("darkMode"); + final var themePref = findPreference("darkMode"); + if (themePref != null) { + updateThemePreferenceSummary(preferences.getDarkThemeMode().name()); - List themeEntries = new ArrayList<>(3); - themeEntries.add(getString(R.string.prefs_value_theme_light)); - themeEntries.add(getString(R.string.prefs_value_theme_dark)); - themeEntries.add(getString(R.string.prefs_value_theme_system)); - - List themeValues = new ArrayList<>(3); - themeValues.add(DarkMode.LIGHT.name()); - themeValues.add(DarkMode.DARK.name()); - themeValues.add(DarkMode.SYSTEM.name()); + themePref.setOnPreferenceClickListener(preference -> { + Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.ThemeSelection); + startActivityForResult(intent, ExtendedSettingsActivityDialog.ThemeSelection.getResultId()); + return true; + }); + } + } - themePref.setEntries(themeEntries.toArray(new String[0])); - themePref.setEntryValues(themeValues.toArray(new String[0])); + private void updateThemePreferenceSummary(String themeValue) { + Preference themePref = findPreference("darkMode"); + if (themePref == null) return; - if (TextUtils.isEmpty(themePref.getEntry())) { - themePref.setValue(DarkMode.SYSTEM.name()); - themePref.setSummary(TextUtils.isEmpty(themePref.getEntry()) ? DarkMode.SYSTEM.name() : themePref.getEntry()); + DarkMode mode; + try { + mode = DarkMode.valueOf(themeValue); + } catch (IllegalArgumentException e) { + mode = DarkMode.SYSTEM; } - themePref.setOnPreferenceChangeListener((preference, newValue) -> { - DarkMode mode = DarkMode.valueOf((String) newValue); - preferences.setDarkThemeMode(mode); - MainApp.setAppTheme(mode); - setListBackground(); + String summary = switch (mode) { + case LIGHT -> getString(R.string.prefs_value_theme_light); + case DARK -> getString(R.string.prefs_value_theme_dark); + default -> getString(R.string.prefs_value_theme_system); + }; - return true; - }); + themePref.setSummary(summary); } private void setListBackground() { @@ -1052,14 +1052,21 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == ACTION_E2E && data != null && data.getBooleanExtra(SetupEncryptionDialogFragment.SUCCESS, false)) { Intent i = new Intent(this, SettingsActivity.class); startActivity(i); - } else if (requestCode == ACTION_SET_STORAGE_LOCATION && data != null) { - String newPath = data.getStringExtra(ChooseStorageLocationActivity.KEY_RESULT_STORAGE_LOCATION); - + } else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) { + String newPath = data.getStringExtra(ExtendedSettingsActivityDialog.StorageLocation.getKey()); if (storagePath != null && !storagePath.equals(newPath)) { StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils); storageMigration.setStorageMigrationProgressListener(this); storageMigration.migrate(); } + } else if (requestCode == ExtendedSettingsActivityDialog.ThemeSelection.getResultId() && data != null) { + String selectedTheme = data.getStringExtra(ExtendedSettingsActivityDialog.ThemeSelection.getKey()); + if (selectedTheme != null) { + updateThemePreferenceSummary(selectedTheme); + + // needed for to change status bar color + recreate(); + } } else if (requestCode == REQ_ALL_FILES_ACCESS) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); setupAllFilesAccessPreference(preferenceCategorySync); diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt new file mode 100644 index 000000000000..1d282c6943ab --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt @@ -0,0 +1,100 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.dialog + +import android.app.Dialog +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.setFragmentResult +import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.client.preferences.DarkMode +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.databinding.DialogThemeSelectionBinding +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class ThemeSelectionDialog : + DialogFragment(), + Injectable { + + @Inject + lateinit var preferences: AppPreferences + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private lateinit var binding: DialogThemeSelectionBinding + + override fun onStart() { + super.onStart() + val alertDialog = dialog as AlertDialog + + val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton + positiveButton?.let { + viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogThemeSelectionBinding.inflate(layoutInflater) + + val currentTheme = preferences.getDarkThemeMode() ?: DarkMode.SYSTEM + val radioGroup = binding.themeRadioGroup + + viewThemeUtils.platform.run { + colorTextView(binding.dialogTitle) + themeRadioButton(binding.themeDark) + themeRadioButton(binding.themeLight) + themeRadioButton(binding.themeSystem) + } + + when (currentTheme) { + DarkMode.LIGHT -> radioGroup.check(R.id.theme_light) + DarkMode.DARK -> radioGroup.check(R.id.theme_dark) + DarkMode.SYSTEM -> radioGroup.check(R.id.theme_system) + } + + radioGroup.setOnCheckedChangeListener { _, checkedId -> + val selectedMode = when (checkedId) { + R.id.theme_light -> DarkMode.LIGHT + R.id.theme_dark -> DarkMode.DARK + R.id.theme_system -> DarkMode.SYSTEM + else -> DarkMode.SYSTEM + } + + applyTheme(selectedMode) + } + + val builder = MaterialAlertDialogBuilder(requireContext()) + .setView(binding.root) + .setPositiveButton(R.string.common_ok) { _, _ -> + dismiss() + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder) + + return builder.create() + } + + private fun applyTheme(mode: DarkMode) { + preferences.setDarkThemeMode(mode) + MainApp.setAppTheme(mode) + + setFragmentResult( + ExtendedSettingsActivityDialog.ThemeSelection.key, + bundleOf(ExtendedSettingsActivityDialog.ThemeSelection.key to mode.name) + ) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt new file mode 100644 index 000000000000..4316d78f7755 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt @@ -0,0 +1,50 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.model + +import android.app.Activity.RESULT_OK +import android.content.Intent +import com.nextcloud.ui.ChooseStorageLocationDialogFragment +import com.owncloud.android.ui.activity.ExtendedSettingsActivity +import com.owncloud.android.ui.dialog.ThemeSelectionDialog + +@Suppress("MagicNumber") +enum class ExtendedSettingsActivityDialog(val tag: String, val key: String, val resultId: Int) { + StorageLocation("choose_storage_location", "storage_selection_result", 13), + ThemeSelection("theme_selection", "theme_selection_result", 14); + + fun showDialog(activity: ExtendedSettingsActivity) { + activity.run { + if (supportFragmentManager.findFragmentByTag(tag) != null) { + return + } + + supportFragmentManager.setFragmentResultListener( + key, + this + ) { _, result -> + setResult( + RESULT_OK, + Intent().putExtra( + key, + result.getString(key) + ) + ) + finish() + } + + if (this@ExtendedSettingsActivityDialog == StorageLocation) { + ChooseStorageLocationDialogFragment() + } else { + ThemeSelectionDialog() + }.run { + show(supportFragmentManager, tag) + } + } + } +} diff --git a/app/src/main/res/layout/dialog_theme_selection.xml b/app/src/main/res/layout/dialog_theme_selection.xml new file mode 100644 index 000000000000..447ccc7c83eb --- /dev/null +++ b/app/src/main/res/layout/dialog_theme_selection.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index adada5180474..ff17922ff1f8 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -17,7 +17,7 @@ android:title="@string/prefs_data_storage_location" android:key="data_storage_location" android:summary="@string/prefs_data_storage_location_summary" /> -