From f1c410deaa7f686a37dc4d720c92cb8f308b802d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 29 Sep 2023 13:59:27 +0200 Subject: [PATCH] Added light/dark/auto theme setting --- .../java/org/linphone/core/CorePreferences.kt | 15 +++++ .../settings/fragment/SettingsFragment.kt | 38 +++++++++++++ .../settings/viewmodel/SettingsViewModel.kt | 25 ++++++++- app/src/main/res/layout/settings_fragment.xml | 55 ++++++++++++++++--- app/src/main/res/values/strings.xml | 4 ++ 5 files changed, 129 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index 7c6c2db1b..e2141d9de 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -86,6 +86,21 @@ class CorePreferences @UiThread constructor(private val context: Context) { config.setBool("app", "auto_start_call_record", value) } + /** -1 means auto, 0 no, 1 yes */ + @get:WorkerThread @set:WorkerThread + var darkMode: Int + get() { + if (!darkModeAllowed) return 0 + return config.getInt("app", "dark_mode", -1) + } + set(value) { + config.setInt("app", "dark_mode", value) + } + + @get:WorkerThread + private val darkModeAllowed: Boolean + get() = config.getBool("app", "dark_mode_allowed", true) + // Will disable chat feature completely @get:WorkerThread val disableChat: Boolean diff --git a/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt b/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt index c56d2fca7..001cd8c68 100644 --- a/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import androidx.annotation.UiThread +import androidx.appcompat.app.AppCompatDelegate import androidx.navigation.navGraphViewModels import org.linphone.R import org.linphone.core.tools.Log @@ -38,6 +39,26 @@ class SettingsFragment : GenericFragment() { } } + private val themeListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + val label = viewModel.availableThemesNames[position] + val value = viewModel.availableThemesValues[position] + Log.i("$TAG Selected theme is now [$label] ($value)") + viewModel.setTheme(value) + + AppCompatDelegate.setDefaultNightMode( + when (value) { + 0 -> AppCompatDelegate.MODE_NIGHT_NO + 1 -> AppCompatDelegate.MODE_NIGHT_YES + else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } + ) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -74,5 +95,22 @@ class SettingsFragment : GenericFragment() { } binding.deviceRingtoneSpinner.onItemSelectedListener = ringtoneListener + + // Light/Dark theme related + val themeAdapter = ArrayAdapter( + requireContext(), + R.layout.drop_down_item, + viewModel.availableThemesNames + ) + themeAdapter.setDropDownViewResource(R.layout.assistant_transport_dropdown_cell) + binding.themeSpinner.adapter = themeAdapter + + viewModel.theme.observe(viewLifecycleOwner) { theme -> + viewModel.availableThemesValues.indexOf( + theme + ) + } + + binding.themeSpinner.onItemSelectedListener = themeListener } } diff --git a/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt index 9887fbc0b..fe60879e8 100644 --- a/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt @@ -50,11 +50,15 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { val videoEnabled = MutableLiveData() val autoInitiateVideoCalls = MutableLiveData() val autoAcceptVideoRequests = MutableLiveData() + val availableRingtonesPaths = arrayListOf() val availableRingtonesNames = arrayListOf() val selectedRingtone = MutableLiveData() val isRingtonePlaying = MutableLiveData() + + val isVibrationAvailable = MutableLiveData() val vibrateDuringIncomingCall = MutableLiveData() + val autoRecordCalls = MutableLiveData() // Network settings @@ -62,7 +66,9 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { // User Interface settings - val isVibrationAvailable = MutableLiveData() + val theme = MutableLiveData() + val availableThemesNames = arrayListOf() + val availableThemesValues = arrayListOf(-1, 0, 1) // Other @@ -86,6 +92,16 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { computeAvailableRingtones() + availableThemesNames.add( + AppUtils.getString(R.string.settings_user_interface_auto_theme_label) + ) + availableThemesNames.add( + AppUtils.getString(R.string.settings_user_interface_light_theme_label) + ) + availableThemesNames.add( + AppUtils.getString(R.string.settings_user_interface_dark_theme_label) + ) + coreContext.postOnCoreThread { core -> echoCancellerEnabled.postValue(core.isEchoCancellationEnabled) routeAudioToBluetooth.postValue(corePreferences.routeAudioToBluetoothIfAvailable) @@ -97,6 +113,8 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { useWifiOnly.postValue(core.isWifiOnlyEnabled) selectedRingtone.postValue(core.ring.orEmpty()) + + theme.postValue(corePreferences.darkMode) } } @@ -245,6 +263,11 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { expandUserInterface.value = expandUserInterface.value == false } + @UiThread + fun setTheme(theme: Int) { + corePreferences.darkMode = theme + } + @UiThread private fun computeAvailableRingtones() { availableRingtonesNames.add( diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml index b80eaaed8..52eb1a42e 100644 --- a/app/src/main/res/layout/settings_fragment.xml +++ b/app/src/main/res/layout/settings_fragment.xml @@ -126,7 +126,7 @@ android:visibility="@{viewModel.expandCalls ? View.VISIBLE : View.GONE}" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="21dp" + android:layout_marginTop="20dp" android:layout_marginEnd="16dp" android:checked="@{viewModel.echoCancellerEnabled}" app:layout_constraintEnd_toEndOf="@id/calls_background" @@ -413,7 +413,7 @@ android:visibility="@{viewModel.expandCalls ? View.VISIBLE : View.GONE}" android:layout_width="1dp" android:layout_height="1dp" - android:layout_marginTop="21dp" + android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/auto_record_switch"/> @@ -472,7 +472,7 @@ android:visibility="@{viewModel.expandNetwork ? View.VISIBLE : View.GONE}" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="21dp" + android:layout_marginTop="20dp" android:layout_marginEnd="16dp" android:checked="@{viewModel.useWifiOnly}" app:layout_constraintEnd_toEndOf="@id/network_background" @@ -483,7 +483,7 @@ android:visibility="@{viewModel.expandNetwork ? View.VISIBLE : View.GONE}" android:layout_width="1dp" android:layout_height="1dp" - android:layout_marginTop="21dp" + android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/wifi_only_switch"/> @@ -518,14 +518,55 @@ app:layout_constraintTop_toBottomOf="@id/user_interface" app:layout_constraintBottom_toBottomOf="@id/user_interface_bottom_anchor"/> + + + + + + + app:layout_constraintTop_toBottomOf="@id/theme_spinner"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fb29f4e7..5af5a54de 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -194,6 +194,10 @@ Network Use only Wi-Fi networks User interface + User interface + Dark theme + Light theme + Auto Advanced settings Manage account