diff --git a/app/src/main/java/org/mozilla/fenix/settings/AccountSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/AccountSettingsFragment.kt new file mode 100644 index 0000000000..31cb4b74d9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/AccountSettingsFragment.kt @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.Navigation +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.mozilla.fenix.R +import org.mozilla.fenix.ext.getPreferenceKey +import org.mozilla.fenix.ext.requireComponents +import kotlin.coroutines.CoroutineContext + +class AccountSettingsFragment : PreferenceFragmentCompat(), CoroutineScope { + private lateinit var job: Job + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + job + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + job = Job() + (activity as AppCompatActivity).title = getString(R.string.preferences_account_settings) + (activity as AppCompatActivity).supportActionBar?.show() + } + + override fun onDestroy() { + super.onDestroy() + job.cancel() + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.account_settings_preferences, rootKey) + + val signIn = context?.getPreferenceKey(R.string.pref_key_sign_out) + val preferenceSignOut = findPreference(signIn) + preferenceSignOut.onPreferenceClickListener = getClickListenerForSignOut() + } + + private fun getClickListenerForSignOut(): Preference.OnPreferenceClickListener { + return Preference.OnPreferenceClickListener { + launch { + requireComponents.backgroundServices.accountManager.logout().await() + Navigation.findNavController(view!!).popBackStack() + } + true + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index b8d51591ef..aa61d0ae7f 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -14,6 +14,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.navigation.Navigation import androidx.preference.Preference import androidx.preference.Preference.OnPreferenceClickListener +import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -44,6 +45,7 @@ import org.mozilla.fenix.R.string.pref_key_data_choices import org.mozilla.fenix.R.string.pref_key_about import org.mozilla.fenix.R.string.pref_key_sign_in import org.mozilla.fenix.R.string.pref_key_account +import org.mozilla.fenix.R.string.pref_key_account_category @SuppressWarnings("TooManyFunctions") class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObserver { @@ -54,7 +56,6 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = Job() - setupAccountUI() } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -67,6 +68,7 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse (activity as AppCompatActivity).supportActionBar?.show() generateWordmark() setupPreferences() + setupAccountUI() } @Suppress("ComplexMethod") @@ -100,6 +102,9 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse requireComponents.useCases.tabsUseCases.addTab.invoke(aboutURL, true) navigateToSettingsArticle() } + resources.getString(pref_key_account) -> { + navigateToAccountSettings() + } } return super.onPreferenceTreeClick(preference) } @@ -110,16 +115,6 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse } private fun setupAccountUI() { - val signIn = context?.getPreferenceKey(pref_key_sign_in) - val firefoxAccountKey = context?.getPreferenceKey(pref_key_account) - - val preferenceSignIn = findPreference(signIn) - val preferenceFirefoxAccount = findPreference(firefoxAccountKey) - - preferenceSignIn.isVisible = true - preferenceFirefoxAccount.isVisible = false - preferenceSignIn.onPreferenceClickListener = getClickListenerForSignIn() - val accountManager = requireComponents.backgroundServices.accountManager // Observe account changes to keep the UI up-to-date. accountManager.register(this, owner = this) @@ -127,7 +122,7 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse // TODO an authenticated state will mark 'preferenceSignIn' as invisible; currently that behaviour is non-ideal: // a "sign in" UI will be displayed at first, and then quickly animate away. // Ideally we don't want it to be displayed at all. - accountManager.authenticatedAccount()?.let { setIsAuthenticated(it) } + updateAuthState(accountManager.authenticatedAccount()) accountManager.accountProfile()?.let { updateAccountProfile(it) } } @@ -235,9 +230,14 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse } } + private fun navigateToAccountSettings() { + val directions = SettingsFragmentDirections.actionSettingsFragmentToAccountSettingsFragment() + Navigation.findNavController(view!!).navigate(directions) + } + // --- AccountObserver interfaces --- override fun onAuthenticated(account: FirefoxAccountShaped) { - setIsAuthenticated(account) + updateAuthState(account) } override fun onError(error: Exception) { @@ -248,7 +248,7 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse } override fun onLoggedOut() { - setIsLoggedOut() + updateAuthState() } override fun onProfileUpdated(profile: Profile) { @@ -256,30 +256,31 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse } // --- Account UI helpers --- - private fun setIsAuthenticated(account: FirefoxAccountShaped) { + private fun updateAuthState(account: FirefoxAccountShaped? = null) { val preferenceSignIn = findPreference(context?.getPreferenceKey(pref_key_sign_in)) val preferenceFirefoxAccount = findPreference(context?.getPreferenceKey(pref_key_account)) + val accountPreferenceCategory = + findPreference(context?.getPreferenceKey(pref_key_account_category)) - preferenceSignIn.isVisible = false - preferenceSignIn.onPreferenceClickListener = null - preferenceFirefoxAccount.isVisible = true - } - - private fun setIsLoggedOut() { - val preferenceSignIn = findPreference(context?.getPreferenceKey(pref_key_sign_in)) - val preferenceFirefoxAccount = findPreference(context?.getPreferenceKey(pref_key_account)) - - preferenceSignIn.isVisible = true - - // TODO this isn't quite right, as we'll have an "Account" preference category title still visible on the screen - preferenceFirefoxAccount.isVisible = false - preferenceSignIn.onPreferenceClickListener = getClickListenerForSignIn() + if (account != null) { + preferenceSignIn.isVisible = false + preferenceSignIn.onPreferenceClickListener = null + preferenceFirefoxAccount.isVisible = true + accountPreferenceCategory.isVisible = true + } else { + preferenceSignIn.isVisible = true + preferenceSignIn.onPreferenceClickListener = getClickListenerForSignIn() + preferenceFirefoxAccount.isVisible = false + accountPreferenceCategory.isVisible = false + } } private fun updateAccountProfile(profile: Profile) { - val preferenceFirefoxAccount = findPreference(context?.getPreferenceKey(pref_key_account)) - preferenceFirefoxAccount.title = profile.displayName.orEmpty() - preferenceFirefoxAccount.summary = profile.email.orEmpty() + launch { + val preferenceFirefoxAccount = findPreference(context?.getPreferenceKey(pref_key_account)) + preferenceFirefoxAccount.title = profile.displayName.orEmpty() + preferenceFirefoxAccount.summary = profile.email.orEmpty() + } } companion object { diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 45ffdd959c..fe730c7fd6 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -98,6 +98,8 @@ app:destination="@id/sitePermissionsFragment"/> + @@ -106,4 +108,6 @@ android:label="SitePermissionsFragment"/> + \ No newline at end of file diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 941e060c9b..6620a3ff22 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -26,5 +26,10 @@ pref_key_mozilla_location_service pref_key_fenix_health_report + + pref_key_account_category + pref_key_sync_now + pref_key_sync_history + pref_key_sign_out diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a87eccfeaa..d0d930d217 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -108,6 +108,18 @@ Data choices Leak Canary + + Account Settings + + + + Sync now + + Choose what to sync + + History + + Sign out diff --git a/app/src/main/res/xml/account_settings_preferences.xml b/app/src/main/res/xml/account_settings_preferences.xml new file mode 100644 index 0000000000..40b207780a --- /dev/null +++ b/app/src/main/res/xml/account_settings_preferences.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 42484c98d5..26fea4f0ae 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -13,8 +13,11 @@ android:title="@string/preferences_sign_in" /> + app:iconSpaceReserved="false" + app:isPreferenceVisible="false"> +