1
0
Fork 0

For #2327: Adds error state syncing

master
Sawyer Blatz 2019-05-24 14:18:27 -07:00
parent caa36c31cd
commit fe50e88fc8
20 changed files with 356 additions and 20 deletions

View File

@ -36,6 +36,7 @@ import org.mozilla.fenix.library.history.HistoryFragmentDirections
import org.mozilla.fenix.search.SearchFragmentDirections import org.mozilla.fenix.search.SearchFragmentDirections
import org.mozilla.fenix.settings.PairFragmentDirections import org.mozilla.fenix.settings.PairFragmentDirections
import org.mozilla.fenix.settings.SettingsFragmentDirections import org.mozilla.fenix.settings.SettingsFragmentDirections
import org.mozilla.fenix.settings.SyncProblemFragmentDirections
import org.mozilla.fenix.settings.TurnOnSyncFragmentDirections import org.mozilla.fenix.settings.TurnOnSyncFragmentDirections
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
@ -200,6 +201,8 @@ open class HomeActivity : AppCompatActivity() {
BrowserDirection.FromTurnOnSync -> TurnOnSyncFragmentDirections.actionTurnOnSyncFragmentToBrowserFragment( BrowserDirection.FromTurnOnSync -> TurnOnSyncFragmentDirections.actionTurnOnSyncFragmentToBrowserFragment(
customTabSessionId customTabSessionId
) )
BrowserDirection.FromSyncProblem ->
SyncProblemFragmentDirections.actionSyncProblemFragmentToBrowserFragment(customTabSessionId)
} }
if (sessionObserver == null) if (sessionObserver == null)
sessionObserver = subscribeToSessions() sessionObserver = subscribeToSessions()
@ -288,5 +291,6 @@ open class HomeActivity : AppCompatActivity() {
enum class BrowserDirection { enum class BrowserDirection {
FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks, FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks,
FromBookmarksFolderSelect, FromHistory, FromPair, FromTurnOnSync FromBookmarksFolderSelect, FromHistory, FromPair, FromTurnOnSync,
FromSyncProblem
} }

View File

@ -7,6 +7,7 @@ package org.mozilla.fenix.components.toolbar
import android.content.Context import android.content.Context
import mozilla.components.browser.menu.BrowserMenuBuilder import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.BrowserMenuDivider import mozilla.components.browser.menu.item.BrowserMenuDivider
import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
import mozilla.components.browser.menu.item.BrowserMenuImageText import mozilla.components.browser.menu.item.BrowserMenuImageText
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
import mozilla.components.browser.menu.item.BrowserMenuSwitch import mozilla.components.browser.menu.item.BrowserMenuSwitch
@ -18,7 +19,7 @@ import org.mozilla.fenix.ext.components
class DefaultToolbarMenu( class DefaultToolbarMenu(
private val context: Context, private val context: Context,
private val sessionId: String?, private val hasSyncError: Boolean = false,
private val requestDesktopStateProvider: () -> Boolean = { false }, private val requestDesktopStateProvider: () -> Boolean = { false },
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {} private val onItemTapped: (ToolbarMenu.Item) -> Unit = {}
) : ToolbarMenu { ) : ToolbarMenu {
@ -103,10 +104,21 @@ class DefaultToolbarMenu(
onItemTapped.invoke(ToolbarMenu.Item.Help) onItemTapped.invoke(ToolbarMenu.Item.Help)
}, },
BrowserMenuImageText( BrowserMenuHighlightableItem(
context.getString(R.string.browser_menu_settings), label = context.getString(R.string.browser_menu_settings),
R.drawable.ic_settings, imageResource = R.drawable.ic_settings,
DefaultThemeManager.resolveAttribute(R.attr.primaryText, context) iconTintColorResource = if (hasSyncError)
R.color.sync_error_text_color else
DefaultThemeManager.resolveAttribute(R.attr.primaryText, context),
textColorResource = if (hasSyncError)
R.color.sync_error_text_color else
DefaultThemeManager.resolveAttribute(R.attr.primaryText, context),
highlight = if (hasSyncError) {
BrowserMenuHighlightableItem.Highlight(
imageResource = R.drawable.ic_alert,
backgroundResource = R.color.sync_error_color
)
} else null
) { ) {
onItemTapped.invoke(ToolbarMenu.Item.Settings) onItemTapped.invoke(ToolbarMenu.Item.Settings)
}, },

View File

@ -20,6 +20,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.UIView import org.mozilla.fenix.mvi.UIView
import org.mozilla.fenix.utils.Settings
class ToolbarUIView( class ToolbarUIView(
sessionId: String?, sessionId: String?,
@ -104,7 +105,7 @@ class ToolbarUIView(
) )
} else { } else {
DefaultToolbarMenu(this, DefaultToolbarMenu(this,
sessionId = sessionId, hasSyncError = Settings.getInstance(this).hasSyncProblem,
requestDesktopStateProvider = { session?.desktopMode ?: false }, requestDesktopStateProvider = { session?.desktopMode ?: false },
onItemTapped = { actionEmitter.onNext(SearchAction.ToolbarMenuItemTapped(it)) } onItemTapped = { actionEmitter.onNext(SearchAction.ToolbarMenuItemTapped(it)) }
) )

View File

@ -77,6 +77,7 @@ import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.share.ShareTab import org.mozilla.fenix.share.ShareTab
import org.mozilla.fenix.utils.allowUndo import org.mozilla.fenix.utils.allowUndo
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -682,6 +683,10 @@ class HomeFragment : Fragment(), CoroutineScope, AccountObserver {
Mode.Normal Mode.Normal
} }
override fun onAuthenticationProblems() {
Settings.getInstance(context!!).setHasAuthenticationProblem(true)
emitAccountChanges()
}
override fun onAuthenticated(account: OAuthAccount) { emitAccountChanges() } override fun onAuthenticated(account: OAuthAccount) { emitAccountChanges() }
override fun onError(error: Exception) { emitAccountChanges() } override fun onError(error: Exception) { emitAccountChanges() }
override fun onLoggedOut() { emitAccountChanges() } override fun onLoggedOut() { emitAccountChanges() }

View File

@ -50,6 +50,7 @@ import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.utils.allowUndo import org.mozilla.fenix.utils.allowUndo
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions", "LargeClass") @SuppressWarnings("TooManyFunctions", "LargeClass")
@ -363,6 +364,10 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler, AccountObserve
getManagedEmitter<SignInChange>().onNext(SignInChange.SignedOut) getManagedEmitter<SignInChange>().onNext(SignInChange.SignedOut)
} }
override fun onAuthenticationProblems() {
Settings.getInstance(context!!).setHasAuthenticationProblem(true)
}
override fun onProfileUpdated(profile: Profile) { override fun onProfileUpdated(profile: Profile) {
} }

View File

@ -46,6 +46,7 @@ import org.mozilla.fenix.library.bookmarks.SignInViewModel
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@SuppressWarnings("TooManyFunctions") @SuppressWarnings("TooManyFunctions")
@ -156,6 +157,10 @@ class SelectBookmarkFolderFragment : Fragment(), CoroutineScope, AccountObserver
} }
} }
override fun onAuthenticationProblems() {
Settings.getInstance(context!!).setHasAuthenticationProblem(true)
}
override fun onAuthenticated(account: OAuthAccount) { override fun onAuthenticated(account: OAuthAccount) {
getManagedEmitter<SignInChange>().onNext(SignInChange.SignedIn) getManagedEmitter<SignInChange>().onNext(SignInChange.SignedIn)
} }

View File

@ -0,0 +1,42 @@
/* 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.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R
class AccountPreference : Preference {
var title: TextView? = null
var summary: TextView? = null
var errorIcon: ImageView? = null
var background: View? = null
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, attributeSetId: Int) : super(
context,
attrs,
attributeSetId
)
init {
layoutResource = R.layout.account_preference
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
title = holder.findViewById(R.id.title) as TextView
summary = holder.findViewById(R.id.summary) as TextView
errorIcon = holder.findViewById(R.id.error_icon) as ImageView
background = holder.itemView
}
}

View File

@ -9,8 +9,10 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.Preference.OnPreferenceClickListener
@ -50,6 +52,7 @@ import org.mozilla.fenix.R.string.pref_key_tracking_protection_settings
import org.mozilla.fenix.R.string.pref_key_your_rights import org.mozilla.fenix.R.string.pref_key_your_rights
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getColorFromAttr
import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.utils.ItsNotBrokenSnack import org.mozilla.fenix.utils.ItsNotBrokenSnack
@ -66,12 +69,15 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
job = Job() job = Job()
setupAccountUI() setupAccountUI()
updateSignInVisibility() updateSignInVisibility()
displayAccountErrorIfNecessary()
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener { sharedPreferences, key -> preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
try { try {
context?.let { context?.let {
it.components.analytics.metrics.track(Event.PreferenceToggled it.components.analytics.metrics.track(
(key, sharedPreferences.getBoolean(key, false), it)) Event.PreferenceToggled
(key, sharedPreferences.getBoolean(key, false), it)
)
} }
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
// The event is not tracked // The event is not tracked
@ -123,6 +129,7 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
setupPreferences() setupPreferences()
setupAccountUI() setupAccountUI()
updateSignInVisibility() updateSignInVisibility()
displayAccountErrorIfNecessary()
} }
@Suppress("ComplexMethod") @Suppress("ComplexMethod")
@ -171,7 +178,12 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
navigateToAbout() navigateToAbout()
} }
resources.getString(pref_key_account) -> { resources.getString(pref_key_account) -> {
navigateToAccountSettings() if (org.mozilla.fenix.utils.Settings.getInstance(preference.context).preferences
.getBoolean(context!!.getPreferenceKey(R.string.pref_key_sync_problem), false)) {
navigateToSyncProblem()
} else {
navigateToAccountSettings()
}
} }
resources.getString(pref_key_delete_browsing_data) -> { resources.getString(pref_key_delete_browsing_data) -> {
navigateToDeleteBrowsingData() navigateToDeleteBrowsingData()
@ -302,6 +314,11 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
Navigation.findNavController(view!!).navigate(directions) Navigation.findNavController(view!!).navigate(directions)
} }
private fun navigateToSyncProblem() {
val directions = SettingsFragmentDirections.actionSettingsFragmentToSyncProblemFragment()
Navigation.findNavController(view!!).navigate(directions)
}
private fun navigateToAccountSettings() { private fun navigateToAccountSettings() {
val directions = val directions =
SettingsFragmentDirections.actionSettingsFragmentToAccountSettingsFragment() SettingsFragmentDirections.actionSettingsFragmentToAccountSettingsFragment()
@ -316,6 +333,7 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
override fun onAuthenticated(account: OAuthAccount) { override fun onAuthenticated(account: OAuthAccount) {
updateAuthState(account) updateAuthState(account)
updateSignInVisibility() updateSignInVisibility()
displayAccountErrorIfNecessary()
} }
override fun onError(error: Exception) { override fun onError(error: Exception) {
@ -329,16 +347,25 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
override fun onLoggedOut() { override fun onLoggedOut() {
updateAuthState() updateAuthState()
updateSignInVisibility() updateSignInVisibility()
displayAccountErrorIfNecessary()
} }
override fun onProfileUpdated(profile: Profile) { override fun onProfileUpdated(profile: Profile) {
updateAccountProfile(profile) updateAccountProfile(profile)
} }
override fun onAuthenticationProblems() {
org.mozilla.fenix.utils.Settings.getInstance(context!!).setHasAuthenticationProblem(true)
displayAccountErrorIfNecessary()
}
// --- Account UI helpers --- // --- Account UI helpers ---
private fun updateAuthState(account: OAuthAccount? = null) { private fun updateAuthState(account: OAuthAccount? = null) {
// Cache the user's auth state to improve performance of sign in visibility // Cache the user's auth state to improve performance of sign in visibility
org.mozilla.fenix.utils.Settings.getInstance(context!!).setHasCachedAccount(account != null) org.mozilla.fenix.utils.Settings.getInstance(context!!).setHasCachedAccount(account != null)
// Unset sync problems
org.mozilla.fenix.utils.Settings.getInstance(context!!).setHasAuthenticationProblem(false)
} }
private fun updateSignInVisibility() { private fun updateSignInVisibility() {
@ -363,12 +390,59 @@ class SettingsFragment : PreferenceFragmentCompat(), CoroutineScope, AccountObse
} }
} }
private fun updateAccountProfile(profile: Profile) { private fun updateAccountProfile(profile: Profile, error: Boolean = false) {
launch { launch {
val preferenceFirefoxAccount = val preferenceFirefoxAccount =
findPreference<Preference>(context!!.getPreferenceKey(pref_key_account)) findPreference<AccountPreference>(context!!.getPreferenceKey(pref_key_account))
preferenceFirefoxAccount?.title = profile.displayName.orEmpty()
preferenceFirefoxAccount?.summary = profile.email.orEmpty() preferenceFirefoxAccount?.title?.setTextColor(
R.attr.primaryText.getColorFromAttr(context!!)
)
preferenceFirefoxAccount?.title?.text = profile.displayName.orEmpty()
preferenceFirefoxAccount?.title?.visibility =
if (preferenceFirefoxAccount?.title?.text.isNullOrEmpty()) View.GONE else View.VISIBLE
preferenceFirefoxAccount?.summary?.setTextColor(
R.attr.primaryText.getColorFromAttr(context!!)
)
preferenceFirefoxAccount?.summary?.text = profile.email.orEmpty()
preferenceFirefoxAccount?.icon = ContextCompat.getDrawable(context!!, R.drawable.ic_shortcuts)
preferenceFirefoxAccount?.errorIcon?.visibility = View.GONE
preferenceFirefoxAccount?.background?.background = null
}
}
private fun displayAccountErrorIfNecessary() {
if (!org.mozilla.fenix.utils.Settings.getInstance(context!!).hasSyncProblem) { return }
launch {
val preferenceFirefoxAccount =
findPreference<AccountPreference>(context!!.getPreferenceKey(pref_key_account))
preferenceFirefoxAccount?.title?.setTextColor(
ContextCompat.getColor(
context!!,
R.color.sync_error_text_color
)
)
preferenceFirefoxAccount?.title?.text = context!!.getString(R.string.preferences_account_sync_error)
preferenceFirefoxAccount?.title?.visibility =
if (preferenceFirefoxAccount?.title?.text.isNullOrEmpty()) View.GONE else View.VISIBLE
preferenceFirefoxAccount?.summary?.setTextColor(
ContextCompat.getColor(
context!!,
R.color.sync_error_text_color
)
)
preferenceFirefoxAccount?.summary?.text =
requireComponents.backgroundServices.accountManager.accountProfile()?.email.orEmpty()
preferenceFirefoxAccount?.icon = ContextCompat.getDrawable(context!!, R.drawable.ic_account_warning)
preferenceFirefoxAccount?.errorIcon?.visibility = View.VISIBLE
preferenceFirefoxAccount?.background?.background =
ContextCompat.getDrawable(context!!, R.color.sync_error_color)
} }
} }
} }

View File

@ -0,0 +1,63 @@
/* 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 org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.requireComponents
class SyncProblemFragment : PreferenceFragmentCompat() {
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).title = getString(R.string.sync_reconnect)
(activity as AppCompatActivity).supportActionBar?.show()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.sync_problem, rootKey)
val preferenceSignIn =
findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_sign_in))
val preferenceNewAccount =
findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_create_account))
val preferencePairSignIn =
findPreference<Preference>(context!!.getPreferenceKey(R.string.pref_key_sync_pair))
preferenceSignIn?.onPreferenceClickListener = getClickListenerForSignIn()
preferenceNewAccount?.onPreferenceClickListener = getClickListenerForSignIn()
preferencePairSignIn?.onPreferenceClickListener = getClickListenerForPairing()
}
private fun getClickListenerForSignIn(): Preference.OnPreferenceClickListener {
return Preference.OnPreferenceClickListener {
requireComponents.services.accountsAuthFeature.beginAuthentication()
// TODO The sign-in web content populates session history,
// so pressing "back" after signing in won't take us back into the settings screen, but rather up the
// session history stack.
// We could auto-close this tab once we get to the end of the authentication process?
// Via an interceptor, perhaps.
view?.let {
(activity as HomeActivity).openToBrowser(BrowserDirection.FromSyncProblem)
}
true
}
}
private fun getClickListenerForPairing(): Preference.OnPreferenceClickListener {
return Preference.OnPreferenceClickListener {
val directions = TurnOnSyncFragmentDirections.actionTurnOnSyncFragmentToPairInstructionsFragment()
Navigation.findNavController(view!!).navigate(directions)
true
}
}
}

View File

@ -10,7 +10,6 @@ import android.content.Intent.ACTION_SEND
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -109,7 +108,6 @@ class AppShareItemViewHolder(
init { init {
itemView.setOnClickListener { itemView.setOnClickListener {
Log.d("Jonathan", "${shareItem?.name} clicked.")
shareItem?.let { shareItem?.let {
actionEmitter.onNext(ShareAction.ShareAppClicked(it)) actionEmitter.onNext(ShareAction.ShareAppClicked(it))
} }

View File

@ -155,6 +155,9 @@ class Settings private constructor(context: Context) {
val hasCachedAccount: Boolean val hasCachedAccount: Boolean
get() = preferences.getBoolean(appContext.getPreferenceKey(R.string.pref_key_cached_account), false) get() = preferences.getBoolean(appContext.getPreferenceKey(R.string.pref_key_cached_account), false)
val hasSyncProblem: Boolean
get() = preferences.getBoolean(appContext.getPreferenceKey(R.string.pref_key_sync_problem), false)
private val autoBounceQuickActionSheetCount: Int private val autoBounceQuickActionSheetCount: Int
get() = (preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_bounce_quick_action), 0)) get() = (preferences.getInt(appContext.getPreferenceKey(R.string.pref_key_bounce_quick_action), 0))
@ -235,6 +238,12 @@ class Settings private constructor(context: Context) {
.apply() .apply()
} }
fun setHasAuthenticationProblem(hasProblem: Boolean) {
preferences.edit()
.putBoolean(appContext.getPreferenceKey(R.string.pref_key_sync_problem), hasProblem)
.apply()
}
private val SitePermissionsRules.Action.id: Int private val SitePermissionsRules.Action.id: Int
get() { get() {
return when (this) { return when (this) {

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.477,2 2,6.477 2,12s4.477,10 10,10 10,-4.477 10,-10c-0.006,-5.52 -4.48,-9.994 -10,-10zM12,20c-4.418,0 -8,-3.582 -8,-8s3.582,-8 8,-8 8,3.582 8,8c-0.005,4.416 -3.584,7.995 -8,8zM12,13c0.552,0 1,-0.448 1,-1L13,8c0,-0.552 -0.448,-1 -1,-1s-1,0.448 -1,1v4c0,0.552 0.448,1 1,1zM12,17c0.552,0 1,-0.448 1,-1s-0.448,-1 -1,-1 -1,0.448 -1,1 0.448,1 1,1z"
android:fillColor="@color/sync_error_text_color"/>
</vector>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/account_preference_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:foreground="?android:attr/selectableItemBackground">
<FrameLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.preference.internal.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:maxWidth="48dp"
app:maxHeight="48dp"/>
</FrameLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="6dp"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:layout_weight="1">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?primaryText"
android:singleLine="true"
android:textSize="16sp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:visibility="gone"/>
<TextView android:id="@+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignStart="@id/title"
android:textColor="?primaryText"
android:text="@string/preferences_account_default_name"
android:maxLines="4"/>
</RelativeLayout>
<LinearLayout android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"/>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/error_icon"
android:src="@drawable/ic_alert"
android:padding="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="gone"/>
</LinearLayout>

View File

@ -290,6 +290,8 @@
<action <action
android:id="@+id/action_settingsFragment_to_deleteBrowsingDataFragment" android:id="@+id/action_settingsFragment_to_deleteBrowsingDataFragment"
app:destination="@id/deleteBrowsingDataFragment" /> app:destination="@id/deleteBrowsingDataFragment" />
<action android:id="@+id/action_settingsFragment_to_syncProblemFragment"
app:destination="@id/syncProblemFragment"/>
</fragment> </fragment>
<fragment <fragment
android:id="@+id/dataChoicesFragment" android:id="@+id/dataChoicesFragment"
@ -455,4 +457,8 @@
app:popUpTo="@id/quickSettingsSheetDialogFragment" app:popUpTo="@id/quickSettingsSheetDialogFragment"
app:popUpToInclusive="true" /> app:popUpToInclusive="true" />
</dialog> </dialog>
<fragment android:id="@+id/syncProblemFragment" android:name="org.mozilla.fenix.settings.SyncProblemFragment"
android:label="SyncProblemFragment">
<action android:id="@+id/action_syncProblemFragment_to_browserFragment" app:destination="@id/browserFragment"/>
</fragment>
</navigation> </navigation>

View File

@ -139,6 +139,8 @@
<color name="disabled_text">#cccccc</color> <color name="disabled_text">#cccccc</color>
<color name="violet_05">#E7DFFF</color> <color name="violet_05">#E7DFFF</color>
<color name="text_scale_example_text_color">#232749</color> <color name="text_scale_example_text_color">#232749</color>
<color name="sync_error_color">#FFF36E</color>
<color name="sync_error_text_color">#960E18</color>
<!-- Reader View colors --> <!-- Reader View colors -->
<color name="mozac_feature_readerview_text_color">@color/primary_text_light_theme</color> <color name="mozac_feature_readerview_text_color">@color/primary_text_light_theme</color>

View File

@ -45,6 +45,7 @@
<string name="pref_key_sync_sign_in" translatable="false">pref_key_sync_sign_in</string> <string name="pref_key_sync_sign_in" translatable="false">pref_key_sync_sign_in</string>
<string name="pref_key_sync_create_account" translatable="false">pref_key_sync_create_account</string> <string name="pref_key_sync_create_account" translatable="false">pref_key_sync_create_account</string>
<string name="pref_key_sync_syncing_items" translatable="false">pref_key_sync_syncing_items</string> <string name="pref_key_sync_syncing_items" translatable="false">pref_key_sync_syncing_items</string>
<string name="pref_key_sync_problem" translatable="false">pref_key_sync_problem</string>
<!-- Search Settings --> <!-- Search Settings -->
<string name="pref_key_show_search_suggestions" translatable="false">pref_key_show_search_suggestions</string> <string name="pref_key_show_search_suggestions" translatable="false">pref_key_show_search_suggestions</string>

View File

@ -19,10 +19,9 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:isPreferenceVisible="false"> app:isPreferenceVisible="false">
<androidx.preference.Preference <org.mozilla.fenix.settings.AccountPreference
android:icon="@drawable/ic_shortcuts" android:icon="@drawable/ic_shortcuts"
android:key="@string/pref_key_account" android:key="@string/pref_key_account"/>
android:title="@string/preferences_account_default_name" />
</androidx.preference.PreferenceCategory> </androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.Preference
android:key="@string/pref_key_sync_sign_in"
android:title="@string/preferences_sync_sign_in_to_reconnect"
android:icon="@drawable/ic_sign_in" />
<androidx.preference.Preference
android:key="@string/pref_key_sign_out"
android:title="@string/preferences_sync_remove_account"/>
</androidx.preference.PreferenceScreen>

View File

@ -29,7 +29,7 @@ private object Versions {
const val androidx_transition = "1.1.0-rc01" const val androidx_transition = "1.1.0-rc01"
const val google_material = "1.1.0-alpha06" const val google_material = "1.1.0-alpha06"
const val mozilla_android_components = "0.54.0-SNAPSHOT" const val mozilla_android_components = "0.55.0-SNAPSHOT"
// Note that android-components also depends on application-services, // Note that android-components also depends on application-services,
// and in fact is our main source of appservices-related functionality. // and in fact is our main source of appservices-related functionality.
// The version number below tracks the application-services version // The version number below tracks the application-services version