diff --git a/app/build.gradle b/app/build.gradle index 6426fc31483..623091ccdc5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -164,6 +164,7 @@ configurations.all { dependencies { implementation 'androidx.preference:preference-ktx:1.2.1' + implementation 'androidx.datastore:datastore-core:1.0.0' detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.1") implementation fileTree(include: ['*'], dir: 'libs') @@ -237,7 +238,7 @@ dependencies { implementation "androidx.room:room-ktx:${roomVersion}" implementation "org.parceler:parceler-api:$parcelerVersion" - implementation 'net.orange-box.storebox:storebox-lib:1.4.0' + implementation 'net.orange-box.:-lib:1.4.0' implementation 'eu.davidea:flexible-adapter:5.1.0' implementation 'eu.davidea:flexible-adapter-ui:1.0.0' implementation 'org.apache.commons:commons-lang3:3.13.0' @@ -282,6 +283,7 @@ dependencies { //noinspection DuplicatePlatformClasses exclude group: 'org.apache.httpcomponents', module: 'httpclient' }) + implementation("androidx.datastore:datastore-preferences:1.0.0") implementation 'androidx.core:core-ktx:1.10.1' diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java index 66681797739..ed4625a157c 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java @@ -25,25 +25,33 @@ import com.nextcloud.talk.data.source.local.TalkDatabase; import com.nextcloud.talk.utils.preferences.AppPreferences; - -import net.orange_box.storebox.StoreBox; +import com.nextcloud.talk.utils.preferences.AppPreferencesImpl; import javax.inject.Singleton; import androidx.annotation.NonNull; +import androidx.annotation.OptIn; import dagger.Module; import dagger.Provides; +import kotlinx.coroutines.ExperimentalCoroutinesApi; @Module +@OptIn(markerClass = ExperimentalCoroutinesApi.class) public class DatabaseModule { @Provides @Singleton public AppPreferences providePreferences(@NonNull final Context poContext) { - AppPreferences preferences = StoreBox.create(poContext, AppPreferences.class); + AppPreferences preferences = new AppPreferencesImpl(poContext); preferences.removeLinkPreviews(); return preferences; } + @Provides + @Singleton + public AppPreferencesImpl providePreferencesImpl(@NonNull final Context poContext) { + return new AppPreferencesImpl(poContext); + } + @Provides @Singleton public TalkDatabase provideTalkDatabase(@NonNull final Context context, diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt index 68e4affc9d5..21bd2b28fb4 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt @@ -22,20 +22,26 @@ package com.nextcloud.talk.remotefilebrowser.viewmodels +import android.content.res.Resources import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.R import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepository import com.nextcloud.talk.utils.FileSortOrder import com.nextcloud.talk.utils.Mimetype.FOLDER -import com.nextcloud.talk.utils.preferences.AppPreferences +import com.nextcloud.talk.utils.preferences.AppPreferencesImpl import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch import java.io.File import javax.inject.Inject @@ -52,9 +58,12 @@ import javax.inject.Inject * FinishState --> [*] * @enduml */ -class RemoteFileBrowserItemsViewModel @Inject constructor( +@OptIn(ExperimentalCoroutinesApi::class) +class RemoteFileBrowserItemsViewModel +@Inject +constructor( private val repository: RemoteFileBrowserItemsRepository, - private val appPreferences: AppPreferences + private val appPreferences: AppPreferencesImpl ) : ViewModel() { @@ -66,7 +75,8 @@ class RemoteFileBrowserItemsViewModel @Inject constructor( class FinishState(val selectedPaths: Set) : ViewState private val initialSortOrder = FileSortOrder.getFileSortOrder(appPreferences.sorting) - private val sortingPrefListener: SortChangeListener = SortChangeListener() + + private var sortingFlow: Flow private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData @@ -86,20 +96,15 @@ class RemoteFileBrowserItemsViewModel @Inject constructor( get() = _selectedPaths init { - appPreferences.registerSortingChangeListener(sortingPrefListener) - } - - inner class SortChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: String) { - onSelectSortOrder(newValue) + val key = Resources.getSystem().getString(R.string.nc_file_browser_sort_by_key) + sortingFlow = appPreferences.readString(key) + CoroutineScope(Dispatchers.Main).launch { + sortingFlow.collect { newString -> + onSelectSortOrder(newString) + } } } - override fun onCleared() { - super.onCleared() - appPreferences.unregisterSortingChangeListener(sortingPrefListener) - } - fun loadItems() { _viewState.value = LoadingItemsState repository.listFolder(currentPath.value!!).subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index acf24792bab..e065d462d9a 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -27,7 +27,6 @@ package com.nextcloud.talk.settings import android.animation.Animator import android.animation.AnimatorListenerAdapter -import android.app.Activity import android.app.KeyguardManager import android.content.Context import android.content.DialogInterface @@ -60,7 +59,6 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager import autodagger.AutoInjector -import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputLayout @@ -91,12 +89,17 @@ import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.SecurityUtils import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew +import com.nextcloud.talk.utils.preferences.AppPreferencesImpl import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import java.net.URI @@ -104,6 +107,7 @@ import java.net.URISyntaxException import java.util.Locale import javax.inject.Inject +@Suppress("TooManyFunctions", "LargeClass") @AutoInjector(NextcloudTalkApplication::class) class SettingsActivity : BaseActivity() { private lateinit var binding: ActivitySettingsBinding @@ -119,15 +123,15 @@ class SettingsActivity : BaseActivity() { private var currentUser: User? = null private var credentials: String? = null - private var proxyTypeChangeListener: OnPreferenceValueChangedListener? = null - private var proxyCredentialsChangeListener: OnPreferenceValueChangedListener? = null - private var screenSecurityChangeListener: OnPreferenceValueChangedListener? = null - private var screenLockChangeListener: OnPreferenceValueChangedListener? = null - private var screenLockTimeoutChangeListener: OnPreferenceValueChangedListener? = null - private var themeChangeListener: OnPreferenceValueChangedListener? = null - private var readPrivacyChangeListener: OnPreferenceValueChangedListener? = null - private var typingStatusChangeListener: OnPreferenceValueChangedListener? = null - private var phoneBookIntegrationChangeListener: OnPreferenceValueChangedListener? = null + private lateinit var proxyTypeFlow: Flow + private lateinit var proxyCredentialFlow: Flow + private lateinit var screenSecurityFlow: Flow + private lateinit var screenLockFlow: Flow + private lateinit var screenLockTimeoutFlow: Flow + private lateinit var themeFlow: Flow + private lateinit var readPrivacyFlow: Flow + private lateinit var typingStatusFlow: Flow + private lateinit var phoneBookIntegrationFlow: Flow private var profileQueryDisposable: Disposable? = null private var dbQueryDisposable: Disposable? = null @@ -381,59 +385,53 @@ class SettingsActivity : BaseActivity() { } } + @OptIn(ExperimentalCoroutinesApi::class) private fun registerChangeListeners() { - appPreferences.registerProxyTypeListener(ProxyTypeChangeListener().also { proxyTypeChangeListener = it }) - appPreferences.registerProxyCredentialsListener( - ProxyCredentialsChangeListener().also { - proxyCredentialsChangeListener = it - } - ) - appPreferences.registerScreenSecurityListener( - ScreenSecurityChangeListener().also { - screenSecurityChangeListener = it - } + val appPreferences = AppPreferencesImpl(context) + proxyTypeFlow = appPreferences.readString(AppPreferencesImpl.PROXY_TYPE) + proxyCredentialFlow = appPreferences.readBoolean(AppPreferencesImpl.PROXY_CRED) + screenSecurityFlow = appPreferences.readBoolean(AppPreferencesImpl.SCREEN_SECURITY) + screenLockFlow = appPreferences.readBoolean(AppPreferencesImpl.SCREEN_LOCK) + screenLockTimeoutFlow = appPreferences.readString(AppPreferencesImpl.SCREEN_LOCK_TIMEOUT) + + val themeKey = context.resources.getString(R.string.nc_settings_theme_key) + themeFlow = appPreferences.readString(themeKey) + + val privacyKey = context.resources.getString(R.string.nc_settings_read_privacy_key) + readPrivacyFlow = appPreferences.readBoolean(privacyKey) + + typingStatusFlow = appPreferences.readBoolean(AppPreferencesImpl.TYPING_STATUS) + phoneBookIntegrationFlow = appPreferences.readBoolean(AppPreferencesImpl.PHONE_BOOK_INTEGRATION) + + var pos = resources.getStringArray(R.array.screen_lock_timeout_entry_values).indexOf( + appPreferences.screenLockTimeout ) - var pos = getStringArray(R.array.screen_lock_timeout_entry_values).indexOf(appPreferences.screenLockTimeout) binding.settingsScreenLockTimeoutLayoutDropdown.setText( - getStringArray(R.array.screen_lock_timeout_descriptions)[pos] + resources.getStringArray(R.array.screen_lock_timeout_descriptions)[pos] ) + binding.settingsScreenLockTimeoutLayoutDropdown.setSimpleItems(R.array.screen_lock_timeout_descriptions) binding.settingsScreenLockTimeoutLayoutDropdown.setOnItemClickListener { _, _, position, _ -> val entryVal: String = resources.getStringArray(R.array.screen_lock_timeout_entry_values)[position] appPreferences.screenLockTimeout = entryVal + SecurityUtils.createKey(entryVal) } - appPreferences.registerScreenLockListener(ScreenLockListener().also { screenLockChangeListener = it }) - appPreferences.registerScreenLockTimeoutListener( - ScreenLockTimeoutListener().also { - screenLockTimeoutChangeListener = it - } - ) - pos = getStringArray(R.array.theme_entry_values).indexOf(appPreferences.theme) - binding.settingsTheme.setText(getStringArray(R.array.theme_descriptions)[pos]) + pos = resources.getStringArray(R.array.theme_entry_values).indexOf(appPreferences.theme) + binding.settingsTheme.setText(resources.getStringArray(R.array.theme_descriptions)[pos]) + binding.settingsTheme.setSimpleItems(R.array.theme_descriptions) binding.settingsTheme.setOnItemClickListener { _, _, position, _ -> - val entryVal: String = getStringArray(R.array.theme_entry_values)[position] + val entryVal: String = resources.getStringArray(R.array.theme_entry_values)[position] appPreferences.theme = entryVal } - appPreferences.registerThemeChangeListener(ThemeChangeListener().also { themeChangeListener = it }) - appPreferences.registerPhoneBookIntegrationChangeListener( - PhoneBookIntegrationChangeListener(this).also { - phoneBookIntegrationChangeListener = it - } - ) - appPreferences.registerReadPrivacyChangeListener( - ReadPrivacyChangeListener().also { - readPrivacyChangeListener = it - } - ) - binding.settingsPrivacy.setOnClickListener { - readPrivacyChangeListener!!.onChanged(!binding.settingsReadPrivacySwitch.isChecked) - } - appPreferences.registerTypingStatusChangeListener( - TypingStatusChangeListener().also { - typingStatusChangeListener = it - } - ) + + observeProxyType() + observeProxyCredential() + observeScreenSecurity() + observeScreenLock() + observeTheme() + observeReadPrivacy() + observeTypingStatus() } fun sendLogs() { @@ -526,10 +524,10 @@ class SettingsActivity : BaseActivity() { } binding.settingsProxyChoice.setText(appPreferences.proxyType) binding.settingsProxyChoice.setSimpleItems(R.array.proxy_type_descriptions) - binding.settingsProxyChoice.setOnItemClickListener { _, _, position, _ -> - val entryVal = getStringArray(R.array.proxy_type_descriptions)[position] - appPreferences.proxyType = entryVal - } + // binding.settingsProxyChoice.setOnItemClickListener { _, _, position, _ -> + // val entryVal = resources.getStringArray(R.array.proxy_type_descriptions)[position] + // appPreferences.proxyType = entryVal + // } binding.settingsProxyHostEdit.setText(appPreferences.proxyHost) binding.settingsProxyHostEdit.setOnFocusChangeListener { _, hasFocus -> @@ -738,6 +736,13 @@ class SettingsActivity : BaseActivity() { val isChecked = binding.settingsPhoneBookIntegrationSwitch.isChecked binding.settingsPhoneBookIntegrationSwitch.isChecked = !isChecked appPreferences.setPhoneBookIntegration(!isChecked) + if (!isChecked) { + if (checkPermission(this@SettingsActivity, (context))) { + checkForPhoneNumber() + } + } else { + deleteAll() + } } binding.settingsScreenSecurity.setOnClickListener { @@ -798,15 +803,15 @@ class SettingsActivity : BaseActivity() { } public override fun onDestroy() { - appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener) - appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener) - appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener) - appPreferences.unregisterScreenLockListener(screenLockChangeListener) - appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener) - appPreferences.unregisterThemeChangeListener(themeChangeListener) - appPreferences.unregisterReadPrivacyChangeListener(readPrivacyChangeListener) - appPreferences.unregisterTypingStatusChangeListener(typingStatusChangeListener) - appPreferences.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener) + // appPreferences.unregisterProxyTypeListener(proxyTypeChangeListener) + // appPreferences.unregisterProxyCredentialsListener(proxyCredentialsChangeListener) + // appPreferences.unregisterScreenSecurityListener(screenSecurityChangeListener) + // appPreferences.unregisterScreenLockListener(screenLockChangeListener) + // appPreferences.unregisterScreenLockTimeoutListener(screenLockTimeoutChangeListener) + // appPreferences.unregisterThemeChangeListener(themeChangeListener) + // appPreferences.unregisterReadPrivacyChangeListener(readPrivacyChangeListener) + // appPreferences.unregisterTypingStatusChangeListener(typingStatusChangeListener) + // appPreferences.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener) super.onDestroy() } @@ -896,86 +901,78 @@ class SettingsActivity : BaseActivity() { } } - private inner class ScreenLockTimeoutListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: String?) { - SecurityUtils.createKey(appPreferences.screenLockTimeout) - } - } - - private inner class ScreenLockListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: Boolean) { - binding.settingsScreenLockTimeout.isEnabled = newValue - if (newValue) { - binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA - } else { - binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA + private fun observeScreenLock() { + CoroutineScope(Dispatchers.Main).launch { + screenLockFlow.collect { newBoolean -> + binding.settingsScreenLockTimeout.isEnabled = newBoolean + if (newBoolean) { + binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA + } else { + binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA + } } } } - private inner class ScreenSecurityChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: Boolean) { - if (newValue) { - window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + private fun observeScreenSecurity() { + CoroutineScope(Dispatchers.Main).launch { + screenSecurityFlow.collect { newBoolean -> + if (newBoolean) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } } } } - private inner class ProxyCredentialsChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: Boolean) { - if (newValue) { - showProxyCredentials() - } else { - hideProxyCredentials() + private fun observeProxyCredential() { + CoroutineScope(Dispatchers.Main).launch { + proxyCredentialFlow.collect { newBoolean -> + if (newBoolean) { + showProxyCredentials() + } else { + hideProxyCredentials() + } } } } - private inner class ProxyTypeChangeListener : OnPreferenceValueChangedListener { - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onChanged(newValue: String) { - if (("No proxy" == newValue)) { - hideProxySettings() - } else { - when (newValue) { - "HTTP" -> { - binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_http_value)) - appPreferences.proxyPort = getString(R.string.nc_settings_http_value) - } + private fun observeProxyType() { + CoroutineScope(Dispatchers.Main).launch { + proxyTypeFlow.collect { newString -> + if (("No proxy" == newString)) { + hideProxySettings() + } else { + when (newString) { + "HTTP" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_http_value)) + appPreferences.proxyPort = getString(R.string.nc_settings_http_value) + } - "DIRECT" -> { - binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_direct_value)) - appPreferences.proxyPort = getString(R.string.nc_settings_direct_value) - } - "SOCKS" -> { - binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_socks_value)) - appPreferences.proxyPort = getString(R.string.nc_settings_socks_value) - } - else -> { + "DIRECT" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_direct_value)) + appPreferences.proxyPort = getString(R.string.nc_settings_direct_value) + } + + "SOCKS" -> { + binding.settingsProxyPortEdit.setText(getString(R.string.nc_settings_socks_value)) + appPreferences.proxyPort = getString(R.string.nc_settings_socks_value) + } + + else -> { + } } + showProxySettings() } - showProxySettings() } } } - private inner class ThemeChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: String?) { - setAppTheme((newValue)!!) - } - } - - private inner class PhoneBookIntegrationChangeListener(private val activity: Activity) : - OnPreferenceValueChangedListener { - override fun onChanged(isEnabled: Boolean) { - if (isEnabled) { - if (checkPermission(activity, (context))) { - checkForPhoneNumber() - } - } else { - deleteAll() + private fun observeTheme() { + CoroutineScope(Dispatchers.Main).launch { + themeFlow.collect { newString -> + setAppTheme(newString) } } } @@ -1110,68 +1107,72 @@ class SettingsActivity : BaseActivity() { }) } - private inner class ReadPrivacyChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: Boolean) { - val booleanValue = if (newValue) "0" else "1" - val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}" - ncApi.setReadStatusPrivacy( - ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), - json.toRequestBody("application/json".toMediaTypeOrNull()) - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + private fun observeReadPrivacy() { + CoroutineScope(Dispatchers.Main).launch { + readPrivacyFlow.collect { newBoolean -> + val booleanValue = if (newBoolean) "0" else "1" + val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}" + ncApi.setReadStatusPrivacy( + ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), + ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), + json.toRequestBody("application/json".toMediaTypeOrNull()) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - // unused atm - } + override fun onNext(genericOverall: GenericOverall) { + // unused atm + } - override fun onError(e: Throwable) { - appPreferences.setReadPrivacy(!newValue) - binding.settingsReadPrivacySwitch.isChecked = !newValue - } + override fun onError(e: Throwable) { + appPreferences.setReadPrivacy(!newBoolean) + binding.settingsReadPrivacySwitch.isChecked = !newBoolean + } - override fun onComplete() { - // unused atm - } - }) + override fun onComplete() { + // unused atm + } + }) + } } } - private inner class TypingStatusChangeListener : OnPreferenceValueChangedListener { - override fun onChanged(newValue: Boolean) { - val booleanValue = if (newValue) "0" else "1" - val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}" - ncApi.setTypingStatusPrivacy( - ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), - json.toRequestBody("application/json".toMediaTypeOrNull()) - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + private fun observeTypingStatus() { + CoroutineScope(Dispatchers.Main).launch { + typingStatusFlow.collect { newBoolean -> + val booleanValue = if (newBoolean) "0" else "1" + val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}" + ncApi.setTypingStatusPrivacy( + ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), + ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), + json.toRequestBody("application/json".toMediaTypeOrNull()) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - loadCapabilitiesAndUpdateSettings() - Log.i(TAG, "onNext called typing status set") - } + override fun onNext(genericOverall: GenericOverall) { + loadCapabilitiesAndUpdateSettings() + Log.i(TAG, "onNext called typing status set") + } - override fun onError(e: Throwable) { - appPreferences.setTypingStatus(!newValue) - binding.settingsTypingStatusSwitch.isChecked = !newValue - } + override fun onError(e: Throwable) { + appPreferences.setTypingStatus(!newBoolean) + binding.settingsTypingStatusSwitch.isChecked = !newBoolean + } - override fun onComplete() { - // unused atm - } - }) + override fun onComplete() { + // unused atm + } + }) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java index 62cdd084d4f..84f82f85bbd 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java @@ -26,333 +26,143 @@ import android.annotation.SuppressLint; -import com.nextcloud.talk.R; - -import net.orange_box.storebox.annotations.method.ClearMethod; -import net.orange_box.storebox.annotations.method.DefaultValue; -import net.orange_box.storebox.annotations.method.KeyByResource; -import net.orange_box.storebox.annotations.method.KeyByString; -import net.orange_box.storebox.annotations.method.RegisterChangeListenerMethod; -import net.orange_box.storebox.annotations.method.RemoveMethod; -import net.orange_box.storebox.annotations.method.UnregisterChangeListenerMethod; -import net.orange_box.storebox.annotations.option.SaveOption; -import net.orange_box.storebox.enums.SaveMode; -import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener; - @SuppressLint("NonConstantResourceId") -@SaveOption(SaveMode.APPLY) public interface AppPreferences { - @KeyByString("proxy_type") - @RegisterChangeListenerMethod - void registerProxyTypeListener(OnPreferenceValueChangedListener listener); - - @KeyByString("proxy_type") - @UnregisterChangeListenerMethod - void unregisterProxyTypeListener(OnPreferenceValueChangedListener listener); - - @KeyByString("proxy_type") String getProxyType(); - @KeyByString("proxy_type") void setProxyType(String proxyType); - @KeyByString("proxy_server") - @RemoveMethod void removeProxyType(); - @KeyByString("proxy_host") String getProxyHost(); - @KeyByString("proxy_host") void setProxyHost(String proxyHost); - @KeyByString("proxy_host") - @RemoveMethod void removeProxyHost(); - @KeyByString("proxy_port") String getProxyPort(); - @KeyByString("proxy_port") void setProxyPort(String proxyPort); - @KeyByString("proxy_port") - @RemoveMethod void removeProxyPort(); - @KeyByString("proxy_credentials") - @RegisterChangeListenerMethod - void registerProxyCredentialsListener(OnPreferenceValueChangedListener listener); - - @KeyByString("proxy_credentials") - @UnregisterChangeListenerMethod - void unregisterProxyCredentialsListener(OnPreferenceValueChangedListener listener); - - @KeyByString("proxy_credentials") boolean getProxyCredentials(); - @KeyByString("proxy_credentials") void setProxyNeedsCredentials(boolean proxyNeedsCredentials); - @KeyByString("proxy_credentials") - @RemoveMethod void removeProxyCredentials(); - @KeyByString("proxy_username") String getProxyUsername(); - @KeyByString("proxy_username") void setProxyUsername(String proxyUsername); - @KeyByString("proxy_username") - @RemoveMethod void removeProxyUsername(); - @KeyByString("proxy_password") String getProxyPassword(); - @KeyByString("proxy_password") void setProxyPassword(String proxyPassword); - @KeyByString("proxy_password") - @RemoveMethod void removeProxyPassword(); - @KeyByString("push_token") String getPushToken(); - @KeyByString("push_token") void setPushToken(String pushToken); - @KeyByString("push_token") - @RemoveMethod void removePushToken(); - @KeyByString("tempClientCertAlias") String getTemporaryClientCertAlias(); - @KeyByString("tempClientCertAlias") void setTemporaryClientCertAlias(String alias); - @KeyByString("tempClientCertAlias") - @RemoveMethod void removeTemporaryClientCertAlias(); - @KeyByString("pushToTalk_intro_shown") boolean getPushToTalkIntroShown(); - @KeyByString("pushToTalk_intro_shown") void setPushToTalkIntroShown(boolean shown); - @KeyByString("pushToTalk_intro_shown") - @RemoveMethod void removePushToTalkIntroShown(); - @KeyByString("call_ringtone") String getCallRingtoneUri(); - @KeyByString("call_ringtone") void setCallRingtoneUri(String value); - @KeyByString("call_ringtone") - @RemoveMethod void removeCallRingtoneUri(); - @KeyByString("message_ringtone") String getMessageRingtoneUri(); - @KeyByString("message_ringtone") void setMessageRingtoneUri(String value); - @KeyByString("message_ringtone") - @RemoveMethod void removeMessageRingtoneUri(); - @KeyByString("notification_channels_upgrade_to_v2") boolean getIsNotificationChannelUpgradedToV2(); - @KeyByString("notification_channels_upgrade_to_v2") void setNotificationChannelIsUpgradedToV2(boolean value); - @KeyByString("notification_channels_upgrade_to_v2") - @RemoveMethod void removeNotificationChannelUpgradeToV2(); - @KeyByString("notification_channels_upgrade_to_v3") boolean getIsNotificationChannelUpgradedToV3(); - @KeyByString("notification_channels_upgrade_to_v3") void setNotificationChannelIsUpgradedToV3(boolean value); - @KeyByString("notification_channels_upgrade_to_v3") - @RemoveMethod void removeNotificationChannelUpgradeToV3(); - @KeyByString("screen_security") - @DefaultValue(R.bool.value_false) boolean getIsScreenSecured(); - @KeyByString("screen_security") void setScreenSecurity(boolean value); - @KeyByString("screen_security") - @RemoveMethod void removeScreenSecurity(); - @KeyByString("screen_security") - @RegisterChangeListenerMethod - void registerScreenSecurityListener(OnPreferenceValueChangedListener listener); - - @KeyByString("screen_security") - @UnregisterChangeListenerMethod - void unregisterScreenSecurityListener(OnPreferenceValueChangedListener listener); - - @KeyByString("screen_lock") - @DefaultValue(R.bool.value_false) boolean getIsScreenLocked(); - @KeyByString("screen_lock") void setScreenLock(boolean value); - @KeyByString("screen_lock") - @RemoveMethod void removeScreenLock(); - @KeyByString("screen_lock") - @RegisterChangeListenerMethod - void registerScreenLockListener(OnPreferenceValueChangedListener listener); - - @KeyByString("screen_lock") - @UnregisterChangeListenerMethod - void unregisterScreenLockListener(OnPreferenceValueChangedListener listener); - - @KeyByString("incognito_keyboard") - @DefaultValue(R.bool.value_true) boolean getIsKeyboardIncognito(); - @KeyByString("incognito_keyboard") void setIncognitoKeyboard(boolean value); - @KeyByString("incognito_keyboard") - @RemoveMethod void removeIncognitoKeyboard(); - - @KeyByString("phone_book_integration") - @DefaultValue(R.bool.value_false) + boolean isPhoneBookIntegrationEnabled(); - - @KeyByString("phone_book_integration") + void setPhoneBookIntegration(boolean value); // TODO Remove in 13.0.0 - @KeyByString("link_previews") - @RemoveMethod void removeLinkPreviews(); - @KeyByString("screen_lock_timeout") - @DefaultValue(R.string.nc_screen_lock_timeout_sixty) String getScreenLockTimeout(); - @KeyByString("screen_lock_timeout") void setScreenLockTimeout(String value); - @KeyByString("screen_lock_timeout") - @RemoveMethod void removeScreenLockTimeout(); - @KeyByString("screen_lock_timeout") - @RegisterChangeListenerMethod - void registerScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); - - @KeyByString("screen_lock_timeout") - @UnregisterChangeListenerMethod - void unregisterScreenLockTimeoutListener(OnPreferenceValueChangedListener listener); - - @KeyByResource(R.string.nc_settings_theme_key) - @DefaultValue(R.string.nc_default_theme) String getTheme(); - @KeyByResource(R.string.nc_settings_theme_key) void setTheme(String newValue); - @KeyByResource(R.string.nc_settings_theme_key) - @RemoveMethod void removeTheme(); - @KeyByResource(R.string.nc_settings_theme_key) - @RegisterChangeListenerMethod - void registerThemeChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByResource(R.string.nc_settings_theme_key) - @UnregisterChangeListenerMethod - void unregisterThemeChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByString("db_cypher_v4_upgrade") - @DefaultValue(R.bool.value_true) boolean isDbCypherToUpgrade(); - @KeyByString("db_cypher_v4_upgrade") void setDbCypherToUpgrade(boolean value); - @KeyByString("db_room_migrated") - @DefaultValue(R.bool.value_false) boolean getIsDbRoomMigrated(); - @KeyByString("db_room_migrated") void setIsDbRoomMigrated(boolean value); - - @KeyByResource(R.string.nc_settings_phone_book_integration_key) - @RegisterChangeListenerMethod - void registerPhoneBookIntegrationChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByResource(R.string.nc_settings_phone_book_integration_key) - @UnregisterChangeListenerMethod - void unregisterPhoneBookIntegrationChangeListener(OnPreferenceValueChangedListener listener); - @KeyByString("phone_book_integration_last_run") void setPhoneBookIntegrationLastRun(long currentTimeMillis); - @KeyByString("phone_book_integration_last_run") long getPhoneBookIntegrationLastRun(Long defaultValue); - @KeyByResource(R.string.nc_settings_read_privacy_key) void setReadPrivacy(boolean value); - @KeyByString("typing_status") void setTypingStatus(boolean value); - - @KeyByResource(R.string.nc_settings_read_privacy_key) - @RegisterChangeListenerMethod - void registerReadPrivacyChangeListener(OnPreferenceValueChangedListener listener); - @KeyByResource(R.string.nc_settings_read_privacy_key) - @UnregisterChangeListenerMethod - void unregisterReadPrivacyChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByString("typing_status") - @RegisterChangeListenerMethod - void registerTypingStatusChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByString("typing_status") - @UnregisterChangeListenerMethod - void unregisterTypingStatusChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByResource(R.string.nc_file_browser_sort_by_key) void setSorting(String value); - @KeyByResource(R.string.nc_file_browser_sort_by_key) - @DefaultValue(R.string.nc_file_browser_sort_by_default) String getSorting(); - @KeyByResource(R.string.nc_file_browser_sort_by_key) - @RegisterChangeListenerMethod - void registerSortingChangeListener(OnPreferenceValueChangedListener listener); - - @KeyByResource(R.string.nc_file_browser_sort_by_key) - @UnregisterChangeListenerMethod - void unregisterSortingChangeListener(OnPreferenceValueChangedListener listener); - - @ClearMethod void clear(); } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt new file mode 100644 index 00000000000..100669ad389 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt @@ -0,0 +1,483 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.utils.preferences + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.longPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import androidx.lifecycle.asLiveData +import com.nextcloud.talk.R +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking + +@ExperimentalCoroutinesApi +@Suppress("TooManyFunctions", "DeferredResultUnused", "EmptyFunctionBlock") +class AppPreferencesImpl(val context: Context) : AppPreferences { + + override fun getProxyType(): String { + return runBlocking { async { readString(PROXY_TYPE).first() } }.getCompleted() + } + + override fun setProxyType(proxyType: String?) = runBlocking { + async { + if (proxyType != null) { + writeString(PROXY_TYPE, proxyType) + } + } + } + + override fun removeProxyType() { + proxyType = "" + } + + override fun getProxyHost(): String { + return runBlocking { async { readString(PROXY_HOST).first() } }.getCompleted() + } + + override fun setProxyHost(proxyHost: String?) = runBlocking { + async { + if (proxyHost != null) { + writeString(PROXY_HOST, proxyHost) + } + } + } + + override fun removeProxyHost() { + proxyHost = "" + } + + override fun getProxyPort(): String { + return runBlocking { async { readString(PROXY_PORT).first() } }.getCompleted() + } + + override fun setProxyPort(proxyPort: String?) = runBlocking { + async { + if (proxyPort != null) { + writeString(PROXY_PORT, proxyPort) + } + } + } + + override fun removeProxyPort() { + proxyPort = "" + } + + override fun getProxyCredentials(): Boolean { + return runBlocking { async { readBoolean(PROXY_CRED).first() } }.getCompleted() + } + + override fun setProxyNeedsCredentials(proxyNeedsCredentials: Boolean) = runBlocking { + async { + writeBoolean(PROXY_CRED, proxyNeedsCredentials) + } + } + + override fun removeProxyCredentials() { + setProxyNeedsCredentials(false) + } + + override fun getProxyUsername(): String { + return runBlocking { async { readString(PROXY_USERNAME).first() } }.getCompleted() + } + + override fun setProxyUsername(proxyUsername: String?) = runBlocking { + async { + if (proxyUsername != null) { + writeString(PROXY_USERNAME, proxyUsername) + } + } + } + + override fun removeProxyUsername() { + proxyUsername = "" + } + + override fun getProxyPassword(): String { + return runBlocking { async { readString(PROXY_PASSWORD).first() } }.getCompleted() + } + + override fun setProxyPassword(proxyPassword: String?) = runBlocking { + async { + if (proxyPassword != null) { + writeString(PROXY_PASSWORD, proxyPassword) + } + } + } + + override fun removeProxyPassword() { + proxyPassword = "" + } + + override fun getPushToken(): String { + return runBlocking { async { readString(PUSH_TOKEN).first() } }.getCompleted() + } + + override fun setPushToken(pushToken: String?) = runBlocking { + async { + if (pushToken != null) { + writeString(PUSH_TOKEN, pushToken) + } + } + } + + override fun removePushToken() { + pushToken = "" + } + + override fun getTemporaryClientCertAlias(): String { + return runBlocking { async { readString(TEMP_CLIENT_CERT_ALIAS).first() } }.getCompleted() + } + + override fun setTemporaryClientCertAlias(alias: String?) = runBlocking { + async { + if (alias != null) { + writeString(TEMP_CLIENT_CERT_ALIAS, alias) + } + } + } + + override fun removeTemporaryClientCertAlias() { + temporaryClientCertAlias = "" + } + + override fun getPushToTalkIntroShown(): Boolean { + return runBlocking { async { readBoolean(PUSH_TO_TALK_INTRO_SHOWN).first() } }.getCompleted() + } + + override fun setPushToTalkIntroShown(shown: Boolean) = runBlocking { + async { + writeBoolean(PUSH_TO_TALK_INTRO_SHOWN, shown) + } + } + + override fun removePushToTalkIntroShown() { + pushToTalkIntroShown = false + } + + override fun getCallRingtoneUri(): String { + return runBlocking { async { readString(CALL_RINGTONE).first() } }.getCompleted() + } + + override fun setCallRingtoneUri(value: String?) = runBlocking { + async { + if (value != null) { + writeString(CALL_RINGTONE, value) + } + } + } + + override fun removeCallRingtoneUri() { + callRingtoneUri = "" + } + + override fun getMessageRingtoneUri(): String { + return runBlocking { async { readString(MESSAGE_RINGTONE).first() } }.getCompleted() + } + + override fun setMessageRingtoneUri(value: String?) = runBlocking { + async { + if (value != null) { + writeString(MESSAGE_RINGTONE, value) + } + } + } + + override fun removeMessageRingtoneUri() { + messageRingtoneUri = "" + } + + override fun getIsNotificationChannelUpgradedToV2(): Boolean { + return runBlocking { async { readBoolean(NOTIFY_UPGRADE_V2).first() } }.getCompleted() + } + + override fun setNotificationChannelIsUpgradedToV2(value: Boolean) = runBlocking { + async { + writeBoolean(NOTIFY_UPGRADE_V2, value) + } + } + + override fun removeNotificationChannelUpgradeToV2() { + setNotificationChannelIsUpgradedToV2(false) + } + + override fun getIsNotificationChannelUpgradedToV3(): Boolean { + return runBlocking { async { readBoolean(NOTIFY_UPGRADE_V3).first() } }.getCompleted() + } + + override fun setNotificationChannelIsUpgradedToV3(value: Boolean) = runBlocking { + async { + writeBoolean(NOTIFY_UPGRADE_V3, value) + } + } + + override fun removeNotificationChannelUpgradeToV3() { + setNotificationChannelIsUpgradedToV3(false) + } + + override fun getIsScreenSecured(): Boolean { + return runBlocking { async { readBoolean(SCREEN_SECURITY).first() } }.getCompleted() + } + + override fun setScreenSecurity(value: Boolean) = runBlocking { + async { + writeBoolean(SCREEN_SECURITY, value) + } + } + + override fun removeScreenSecurity() { + setScreenSecurity(false) + } + + override fun getIsScreenLocked(): Boolean { + return runBlocking { async { readBoolean(SCREEN_LOCK).first() } }.getCompleted() + } + + override fun setScreenLock(value: Boolean) = runBlocking { + async { + writeBoolean(SCREEN_LOCK, value) + } + } + + override fun removeScreenLock() { + setScreenLock(false) + } + + override fun getIsKeyboardIncognito(): Boolean { + val read = runBlocking { async { readBoolean(INCOGNITO_KEYBOARD).asLiveData().value } }.getCompleted() + return read ?: true + } + + override fun setIncognitoKeyboard(value: Boolean) = runBlocking { + async { + writeBoolean(INCOGNITO_KEYBOARD, value) + } + } + + override fun removeIncognitoKeyboard() { + setIncognitoKeyboard(false) + } + + override fun isPhoneBookIntegrationEnabled(): Boolean { + return runBlocking { async { readBoolean(PHONE_BOOK_INTEGRATION).first() } }.getCompleted() + } + + override fun setPhoneBookIntegration(value: Boolean) = runBlocking { + async { + writeBoolean(PHONE_BOOK_INTEGRATION, value) + } + } + + override fun removeLinkPreviews() = runBlocking { + async { + writeBoolean(LINK_PREVIEWS, false) + } + } + + override fun getScreenLockTimeout(): String { + val default = context.resources.getString(R.string.nc_screen_lock_timeout_sixty) + val read = runBlocking { async { readString(SCREEN_LOCK_TIMEOUT).first() } }.getCompleted() + return read.ifEmpty { default } + } + + override fun setScreenLockTimeout(value: String?) = runBlocking { + async { + if (value != null) { + writeString(SCREEN_LOCK_TIMEOUT, value) + } + } + } + + override fun removeScreenLockTimeout() { + screenLockTimeout = "" + } + + override fun getTheme(): String { + val key = context.resources.getString(R.string.nc_settings_theme_key) + val default = context.resources.getString(R.string.nc_default_theme) + val read = runBlocking { async { readString(key).first() } }.getCompleted() + return read.ifEmpty { default } + } + + override fun setTheme(value: String?) = runBlocking { + async { + if (value != null) { + val key = context.resources.getString(R.string.nc_settings_theme_key) + writeString(key, value) + } + } + } + + override fun removeTheme() { + theme = "" + } + + override fun isDbCypherToUpgrade(): Boolean { + val read = runBlocking { async { readBoolean(DB_CYPHER_V4_UPGRADE).asLiveData().value } }.getCompleted() + return read ?: true + } + + override fun setDbCypherToUpgrade(value: Boolean) = runBlocking { + async { + writeBoolean(DB_CYPHER_V4_UPGRADE, value) + } + } + + override fun getIsDbRoomMigrated(): Boolean { + return runBlocking { async { readBoolean(DB_ROOM_MIGRATED).first() } }.getCompleted() + } + + override fun setIsDbRoomMigrated(value: Boolean) = runBlocking { + async { + writeBoolean(DB_ROOM_MIGRATED, value) + } + } + + override fun setPhoneBookIntegrationLastRun(currentTimeMillis: Long) = runBlocking { + async { + writeLong(PHONE_BOOK_INTEGRATION_LAST_RUN, currentTimeMillis) + } + } + + override fun getPhoneBookIntegrationLastRun(defaultValue: Long?): Long { + val result = if (defaultValue != null) { + runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN, defaultValue = defaultValue).first() } } + .getCompleted() + } else { + runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN).first() } }.getCompleted() + } + return result + } + + override fun setReadPrivacy(value: Boolean) = runBlocking { + val key = context.resources.getString(R.string.nc_settings_read_privacy_key) + async { + writeBoolean(key, value) + } + } + + override fun setTypingStatus(value: Boolean) = runBlocking { + async { + writeBoolean(TYPING_STATUS, value) + } + } + + override fun setSorting(value: String?) = runBlocking { + val key = context.resources.getString(R.string.nc_file_browser_sort_by_key) + async { + if (value != null) { + writeString(key, value) + } + } + } + + override fun getSorting(): String { + val key = context.resources.getString(R.string.nc_file_browser_sort_by_key) + val default = context.resources.getString(R.string.nc_file_browser_sort_by_default) + val read = runBlocking { async { readString(key).first() } }.getCompleted() + return read.ifEmpty { default } + } + + override fun clear() {} + + private suspend fun writeString(key: String, value: String) = context.dataStore.edit { settings -> + settings[ + stringPreferencesKey( + key + ) + ] = value + } + + /** + * Returns a Flow of type String + * @param key the key of the persisted data to be observed + */ + fun readString(key: String, defaultValue: String = ""): Flow = context.dataStore.data.map { preferences -> + preferences[stringPreferencesKey(key)] ?: defaultValue + } + + private suspend fun writeBoolean(key: String, value: Boolean) = context.dataStore.edit { settings -> + settings[ + booleanPreferencesKey( + key + ) + ] = value + } + + /** + * Returns a Flow of type Boolean + * @param key the key of the persisted data to be observed + */ + fun readBoolean(key: String, defaultValue: Boolean = false): Flow = + context.dataStore.data.map { preferences -> + preferences[booleanPreferencesKey(key)] ?: defaultValue + } + + private suspend fun writeLong(key: String, value: Long) = context.dataStore.edit { settings -> + settings[ + longPreferencesKey( + key + ) + ] = value + } + + private fun readLong(key: String, defaultValue: Long = 0): Flow = context.dataStore.data.map { preferences -> + preferences[longPreferencesKey(key)] ?: defaultValue + } + + companion object { + @Suppress("UnusedPrivateProperty") + private val TAG = AppPreferencesImpl::class.simpleName + private val Context.dataStore: DataStore by preferencesDataStore(name = "settings") + const val PROXY_TYPE = "proxy_type" + const val PROXY_SERVER = "proxy_server" + const val PROXY_HOST = "proxy_host" + const val PROXY_PORT = "proxy_port" + const val PROXY_CRED = "proxy_credentials" + const val PROXY_USERNAME = "proxy_username" + const val PROXY_PASSWORD = "proxy_password" + const val PUSH_TOKEN = "push_token" + const val TEMP_CLIENT_CERT_ALIAS = "tempClientCertAlias" + const val PUSH_TO_TALK_INTRO_SHOWN = "pushToTalk_intro_shown" + const val CALL_RINGTONE = "call_ringtone" + const val MESSAGE_RINGTONE = "message_ringtone" + const val NOTIFY_UPGRADE_V2 = "notification_channels_upgrade_to_v2" + const val NOTIFY_UPGRADE_V3 = "notification_channels_upgrade_to_v3" + const val SCREEN_SECURITY = "screen_security" + const val SCREEN_LOCK = "screen_lock" + const val INCOGNITO_KEYBOARD = "incognito_keyboard" + const val PHONE_BOOK_INTEGRATION = "phone_book_integration" + const val LINK_PREVIEWS = "link_previews" + const val SCREEN_LOCK_TIMEOUT = "screen_lock_timeout" + const val DB_CYPHER_V4_UPGRADE = "db_cypher_v4_upgrade" + const val DB_ROOM_MIGRATED = "db_room_migrated" + const val PHONE_BOOK_INTEGRATION_LAST_RUN = "phone_book_integration_last_run" + const val TYPING_STATUS = "typing_status" + } +}