Skip to content

Commit f1a24eb

Browse files
Merge pull request #16457 from nextcloud/settings-dark-mode-dialog
enhancement(settings): m3 theme selection dialog
2 parents 6d49b7f + 4f73b4a commit f1a24eb

10 files changed

Lines changed: 321 additions & 78 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@
358358
android:exported="false"
359359
android:launchMode="singleInstance" />
360360
<activity
361-
android:name=".ui.activity.ChooseStorageLocationActivity"
361+
android:name=".ui.activity.ExtendedSettingsActivity"
362362
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
363363
android:exported="false"
364364
android:theme="@style/Theme.NoBackground" />

app/src/main/java/com/nextcloud/client/di/ComponentsModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment;
100100
import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
101101
import com.owncloud.android.ui.dialog.TermsOfServiceDialog;
102+
import com.owncloud.android.ui.dialog.ThemeSelectionDialog;
102103
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
103104
import com.owncloud.android.ui.fragment.ExtendedListFragment;
104105
import com.owncloud.android.ui.fragment.FeatureFragment;
@@ -423,6 +424,9 @@ abstract class ComponentsModule {
423424
@ContributesAndroidInjector
424425
abstract ChooseStorageLocationDialogFragment chooseStorageLocationDialogFragment();
425426

427+
@ContributesAndroidInjector
428+
abstract ThemeSelectionDialog themeSelectionDialog();
429+
426430
@ContributesAndroidInjector
427431
abstract SharePasswordDialogFragment sharePasswordDialogFragment();
428432

app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.owncloud.android.datastorage.DataStorageProvider
2525
import com.owncloud.android.datastorage.StoragePoint
2626
import com.owncloud.android.datastorage.StoragePoint.PrivacyType
2727
import com.owncloud.android.datastorage.StoragePoint.StorageType
28+
import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog
2829
import com.owncloud.android.utils.DisplayUtils
2930
import com.owncloud.android.utils.theme.ViewThemeUtils
3031
import java.io.File
@@ -151,19 +152,13 @@ class ChooseStorageLocationDialogFragment :
151152
?: return
152153

153154
val resultBundle = Bundle().apply {
154-
putString(KEY_RESULT_STORAGE_LOCATION, newPath.path)
155+
putString(ExtendedSettingsActivityDialog.StorageLocation.key, newPath.path)
155156
}
156157

157-
parentFragmentManager.setFragmentResult(KEY_RESULT_STORAGE_LOCATION, resultBundle)
158+
parentFragmentManager.setFragmentResult(ExtendedSettingsActivityDialog.StorageLocation.key, resultBundle)
158159
}
159160

160161
companion object {
161-
const val KEY_RESULT_STORAGE_LOCATION = "KEY_RESULT_STORAGE_LOCATION"
162-
const val STORAGE_LOCATION_RESULT_CODE = 100
163-
164-
@JvmStatic
165-
fun newInstance() = ChooseStorageLocationDialogFragment()
166-
167162
@JvmStatic
168163
val TAG: String = Companion::class.java.simpleName
169164
}

app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.ui.activity
9+
10+
import android.content.Context
11+
import android.content.Intent
12+
import android.os.Bundle
13+
import androidx.appcompat.app.AppCompatActivity
14+
import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog
15+
16+
class ExtendedSettingsActivity : AppCompatActivity() {
17+
18+
private var dialogShown = false
19+
20+
@Suppress("ReturnCount")
21+
override fun onCreate(savedInstanceState: Bundle?) {
22+
super.onCreate(savedInstanceState)
23+
24+
if (savedInstanceState != null) {
25+
dialogShown = savedInstanceState.getBoolean(KEY_DIALOG_SHOWN, false)
26+
}
27+
28+
if (dialogShown) {
29+
return
30+
}
31+
32+
val dialogKey = intent.getStringExtra(EXTRA_DIALOG_TYPE) ?: run {
33+
finish()
34+
return
35+
}
36+
37+
val dialogType = ExtendedSettingsActivityDialog.entries.find { it.key == dialogKey } ?: run {
38+
finish()
39+
return
40+
}
41+
42+
dialogType.showDialog(this)
43+
dialogShown = true
44+
}
45+
46+
override fun onSaveInstanceState(outState: Bundle) {
47+
super.onSaveInstanceState(outState)
48+
outState.putBoolean(KEY_DIALOG_SHOWN, dialogShown)
49+
}
50+
51+
companion object {
52+
private const val EXTRA_DIALOG_TYPE = "dialog_type"
53+
private const val KEY_DIALOG_SHOWN = "dialog_shown"
54+
55+
fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent =
56+
Intent(context, ExtendedSettingsActivity::class.java).apply {
57+
putExtra(EXTRA_DIALOG_TYPE, dialogType.key)
58+
}
59+
}
60+
}

app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
7171
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
7272
import com.owncloud.android.ui.helpers.FileOperationsHelper;
73+
import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog;
7374
import com.owncloud.android.utils.ClipboardUtil;
7475
import com.owncloud.android.utils.DeviceCredentialUtils;
7576
import com.owncloud.android.utils.DisplayUtils;
@@ -80,7 +81,6 @@
8081
import com.owncloud.android.utils.theme.ViewThemeUtils;
8182

8283
import java.util.ArrayList;
83-
import java.util.List;
8484
import java.util.Objects;
8585

8686
import javax.inject.Inject;
@@ -121,7 +121,6 @@ public class SettingsActivity extends PreferenceActivity
121121
private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10;
122122
private static final int ACTION_SHOW_MNEMONIC = 11;
123123
private static final int ACTION_E2E = 12;
124-
private static final int ACTION_SET_STORAGE_LOCATION = 13;
125124
private static final int TRUE_VALUE = 1;
126125

127126
private static final String DAV_PATH = "/remote.php/dav";
@@ -879,41 +878,42 @@ private void setupGeneralCategory() {
879878
prefDataLoc = findPreference(AppPreferencesImpl.DATA_STORAGE_LOCATION);
880879
if (prefDataLoc != null) {
881880
prefDataLoc.setOnPreferenceClickListener(p -> {
882-
Intent intent = new Intent(MainApp.getAppContext(), ChooseStorageLocationActivity.class);
883-
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
884-
startActivityForResult(intent, ACTION_SET_STORAGE_LOCATION);
881+
Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.StorageLocation);
882+
startActivityForResult(intent, ExtendedSettingsActivityDialog.StorageLocation.getResultId());
885883
return true;
886884
});
887885
}
888886

889-
ListPreference themePref = (ListPreference) findPreference("darkMode");
887+
final var themePref = findPreference("darkMode");
888+
if (themePref != null) {
889+
updateThemePreferenceSummary(preferences.getDarkThemeMode().name());
890890

891-
List<String> themeEntries = new ArrayList<>(3);
892-
themeEntries.add(getString(R.string.prefs_value_theme_light));
893-
themeEntries.add(getString(R.string.prefs_value_theme_dark));
894-
themeEntries.add(getString(R.string.prefs_value_theme_system));
895-
896-
List<String> themeValues = new ArrayList<>(3);
897-
themeValues.add(DarkMode.LIGHT.name());
898-
themeValues.add(DarkMode.DARK.name());
899-
themeValues.add(DarkMode.SYSTEM.name());
891+
themePref.setOnPreferenceClickListener(preference -> {
892+
Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.ThemeSelection);
893+
startActivityForResult(intent, ExtendedSettingsActivityDialog.ThemeSelection.getResultId());
894+
return true;
895+
});
896+
}
897+
}
900898

901-
themePref.setEntries(themeEntries.toArray(new String[0]));
902-
themePref.setEntryValues(themeValues.toArray(new String[0]));
899+
private void updateThemePreferenceSummary(String themeValue) {
900+
Preference themePref = findPreference("darkMode");
901+
if (themePref == null) return;
903902

904-
if (TextUtils.isEmpty(themePref.getEntry())) {
905-
themePref.setValue(DarkMode.SYSTEM.name());
906-
themePref.setSummary(TextUtils.isEmpty(themePref.getEntry()) ? DarkMode.SYSTEM.name() : themePref.getEntry());
903+
DarkMode mode;
904+
try {
905+
mode = DarkMode.valueOf(themeValue);
906+
} catch (IllegalArgumentException e) {
907+
mode = DarkMode.SYSTEM;
907908
}
908909

909-
themePref.setOnPreferenceChangeListener((preference, newValue) -> {
910-
DarkMode mode = DarkMode.valueOf((String) newValue);
911-
preferences.setDarkThemeMode(mode);
912-
MainApp.setAppTheme(mode);
913-
setListBackground();
910+
String summary = switch (mode) {
911+
case LIGHT -> getString(R.string.prefs_value_theme_light);
912+
case DARK -> getString(R.string.prefs_value_theme_dark);
913+
default -> getString(R.string.prefs_value_theme_system);
914+
};
914915

915-
return true;
916-
});
916+
themePref.setSummary(summary);
917917
}
918918

919919
private void setListBackground() {
@@ -1052,14 +1052,21 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
10521052
} else if (requestCode == ACTION_E2E && data != null && data.getBooleanExtra(SetupEncryptionDialogFragment.SUCCESS, false)) {
10531053
Intent i = new Intent(this, SettingsActivity.class);
10541054
startActivity(i);
1055-
} else if (requestCode == ACTION_SET_STORAGE_LOCATION && data != null) {
1056-
String newPath = data.getStringExtra(ChooseStorageLocationActivity.KEY_RESULT_STORAGE_LOCATION);
1057-
1055+
} else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) {
1056+
String newPath = data.getStringExtra(ExtendedSettingsActivityDialog.StorageLocation.getKey());
10581057
if (storagePath != null && !storagePath.equals(newPath)) {
10591058
StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils);
10601059
storageMigration.setStorageMigrationProgressListener(this);
10611060
storageMigration.migrate();
10621061
}
1062+
} else if (requestCode == ExtendedSettingsActivityDialog.ThemeSelection.getResultId() && data != null) {
1063+
String selectedTheme = data.getStringExtra(ExtendedSettingsActivityDialog.ThemeSelection.getKey());
1064+
if (selectedTheme != null) {
1065+
updateThemePreferenceSummary(selectedTheme);
1066+
1067+
// needed for to change status bar color
1068+
recreate();
1069+
}
10631070
} else if (requestCode == REQ_ALL_FILES_ACCESS) {
10641071
final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync");
10651072
setupAllFilesAccessPreference(preferenceCategorySync);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.ui.dialog
9+
10+
import android.app.Dialog
11+
import android.os.Bundle
12+
import androidx.appcompat.app.AlertDialog
13+
import androidx.core.os.bundleOf
14+
import androidx.fragment.app.DialogFragment
15+
import androidx.fragment.app.setFragmentResult
16+
import com.google.android.material.button.MaterialButton
17+
import com.google.android.material.dialog.MaterialAlertDialogBuilder
18+
import com.nextcloud.client.di.Injectable
19+
import com.nextcloud.client.preferences.AppPreferences
20+
import com.nextcloud.client.preferences.DarkMode
21+
import com.owncloud.android.MainApp
22+
import com.owncloud.android.R
23+
import com.owncloud.android.databinding.DialogThemeSelectionBinding
24+
import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog
25+
import com.owncloud.android.utils.theme.ViewThemeUtils
26+
import javax.inject.Inject
27+
28+
class ThemeSelectionDialog :
29+
DialogFragment(),
30+
Injectable {
31+
32+
@Inject
33+
lateinit var preferences: AppPreferences
34+
35+
@Inject
36+
lateinit var viewThemeUtils: ViewThemeUtils
37+
38+
private lateinit var binding: DialogThemeSelectionBinding
39+
40+
override fun onStart() {
41+
super.onStart()
42+
val alertDialog = dialog as AlertDialog
43+
44+
val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton
45+
positiveButton?.let {
46+
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton)
47+
}
48+
}
49+
50+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
51+
binding = DialogThemeSelectionBinding.inflate(layoutInflater)
52+
53+
val currentTheme = preferences.getDarkThemeMode() ?: DarkMode.SYSTEM
54+
val radioGroup = binding.themeRadioGroup
55+
56+
viewThemeUtils.platform.run {
57+
colorTextView(binding.dialogTitle)
58+
themeRadioButton(binding.themeDark)
59+
themeRadioButton(binding.themeLight)
60+
themeRadioButton(binding.themeSystem)
61+
}
62+
63+
when (currentTheme) {
64+
DarkMode.LIGHT -> radioGroup.check(R.id.theme_light)
65+
DarkMode.DARK -> radioGroup.check(R.id.theme_dark)
66+
DarkMode.SYSTEM -> radioGroup.check(R.id.theme_system)
67+
}
68+
69+
radioGroup.setOnCheckedChangeListener { _, checkedId ->
70+
val selectedMode = when (checkedId) {
71+
R.id.theme_light -> DarkMode.LIGHT
72+
R.id.theme_dark -> DarkMode.DARK
73+
R.id.theme_system -> DarkMode.SYSTEM
74+
else -> DarkMode.SYSTEM
75+
}
76+
77+
applyTheme(selectedMode)
78+
}
79+
80+
val builder = MaterialAlertDialogBuilder(requireContext())
81+
.setView(binding.root)
82+
.setPositiveButton(R.string.common_ok) { _, _ ->
83+
dismiss()
84+
}
85+
86+
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder)
87+
88+
return builder.create()
89+
}
90+
91+
private fun applyTheme(mode: DarkMode) {
92+
preferences.setDarkThemeMode(mode)
93+
MainApp.setAppTheme(mode)
94+
95+
setFragmentResult(
96+
ExtendedSettingsActivityDialog.ThemeSelection.key,
97+
bundleOf(ExtendedSettingsActivityDialog.ThemeSelection.key to mode.name)
98+
)
99+
}
100+
}

0 commit comments

Comments
 (0)