Loading app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt +8 −9 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ import androidx.recyclerview.widget.ListAdapter import mozilla.components.browser.storage.sync.SyncedDeviceTabs import org.mozilla.fenix.sync.SyncedTabsViewHolder.DeviceViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.ErrorViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.SignInViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.TabViewHolder import mozilla.components.browser.storage.sync.Tab as SyncTab import mozilla.components.concept.sync.Device as SyncDevice Loading @@ -28,7 +27,6 @@ class SyncedTabsAdapter( DeviceViewHolder.LAYOUT_ID -> DeviceViewHolder(itemView) TabViewHolder.LAYOUT_ID -> TabViewHolder(itemView) ErrorViewHolder.LAYOUT_ID -> ErrorViewHolder(itemView) SignInViewHolder.LAYOUT_ID -> SignInViewHolder(itemView) else -> throw IllegalStateException() } } Loading @@ -41,7 +39,6 @@ class SyncedTabsAdapter( is AdapterItem.Device -> DeviceViewHolder.LAYOUT_ID is AdapterItem.Tab -> TabViewHolder.LAYOUT_ID is AdapterItem.Error -> ErrorViewHolder.LAYOUT_ID is AdapterItem.SignIn -> SignInViewHolder.LAYOUT_ID } fun updateData(syncedTabs: List<SyncedDeviceTabs>) { Loading @@ -62,7 +59,7 @@ class SyncedTabsAdapter( when (oldItem) { is AdapterItem.Device -> newItem is AdapterItem.Device && oldItem.device.id == newItem.device.id is AdapterItem.Tab, AdapterItem.Error, AdapterItem.SignIn -> is AdapterItem.Tab, is AdapterItem.Error -> oldItem == newItem } Loading @@ -74,7 +71,9 @@ class SyncedTabsAdapter( sealed class AdapterItem { data class Device(val device: SyncDevice) : AdapterItem() data class Tab(val tab: SyncTab) : AdapterItem() data class SignIn(val navController: NavController) : AdapterItem() data class Error(val errorResId: Int) : AdapterItem() data class Error( val descriptionResId: Int, val navController: NavController? = null ) : AdapterItem() } } app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt +29 −11 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import android.util.AttributeSet import android.widget.FrameLayout import androidx.annotation.StringRes import androidx.fragment.app.findFragment import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.component_sync_tabs.view.* Loading @@ -19,6 +20,7 @@ import kotlinx.coroutines.launch import mozilla.components.browser.storage.sync.SyncedDeviceTabs import mozilla.components.feature.syncedtabs.view.SyncedTabsView import org.mozilla.fenix.R import java.lang.IllegalStateException class SyncedTabsLayout @JvmOverloads constructor( context: Context, Loading @@ -45,10 +47,17 @@ class SyncedTabsLayout @JvmOverloads constructor( // We may still be displaying a "loading" spinner, hide it. stopLoading() sync_tabs_status.text = context.getText(stringResourceForError(error)) val navController: NavController? = try { findFragment<SyncedTabsFragment>().findNavController() } catch (exception: IllegalStateException) { null } val descriptionResId = stringResourceForError(error) val errorItem = getErrorItem(navController, error, descriptionResId) synced_tabs_list.visibility = View.GONE sync_tabs_status.visibility = View.VISIBLE val errorList: List<SyncedTabsAdapter.AdapterItem> = listOf(errorItem) adapter.submitList(errorList) synced_tabs_pull_to_refresh.isEnabled = pullToRefreshEnableState(error) } Loading @@ -56,17 +65,11 @@ class SyncedTabsLayout @JvmOverloads constructor( override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) { coroutineScope.launch { synced_tabs_list.visibility = View.VISIBLE sync_tabs_status.visibility = View.GONE adapter.updateData(syncedTabs) } } override fun startLoading() { synced_tabs_list.visibility = View.VISIBLE sync_tabs_status.visibility = View.GONE synced_tabs_pull_to_refresh.isRefreshing = true } Loading @@ -80,7 +83,8 @@ class SyncedTabsLayout @JvmOverloads constructor( } companion object { internal fun pullToRefreshEnableState(error: SyncedTabsView.ErrorType) = when (error) { private fun pullToRefreshEnableState(error: SyncedTabsView.ErrorType) = when (error) { // Disable "pull-to-refresh" when we clearly can't sync tabs, and user needs to take an // action within the app. SyncedTabsView.ErrorType.SYNC_UNAVAILABLE, Loading @@ -93,12 +97,26 @@ class SyncedTabsLayout @JvmOverloads constructor( SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> true } internal fun stringResourceForError(error: SyncedTabsView.ErrorType) = when (error) { private fun stringResourceForError(error: SyncedTabsView.ErrorType) = when (error) { SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_sign_in_message SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs } private fun getErrorItem( navController: NavController?, error: SyncedTabsView.ErrorType, @StringRes stringResId: Int ): SyncedTabsAdapter.AdapterItem = when (error) { SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE, SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE, SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION, SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> SyncedTabsAdapter.AdapterItem .Error(descriptionResId = stringResId) SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> SyncedTabsAdapter.AdapterItem .Error(descriptionResId = stringResId, navController = navController) } } } app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt +19 −29 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ package org.mozilla.fenix.sync import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.no_content_message_with_action.view.* import kotlinx.android.synthetic.main.sync_tabs_error_row.view.* import kotlinx.android.synthetic.main.sync_tabs_list_item.view.* import kotlinx.android.synthetic.main.view_synced_tabs_group.view.* import mozilla.components.browser.storage.sync.Tab Loading Loading @@ -44,41 +44,26 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item } } class SignInViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { override fun <T : AdapterItem> bind(item: T, interactor: (Tab) -> Unit) { val signInItem = item as AdapterItem.SignIn val errorItem = item as AdapterItem.Error setErrorMargins() itemView.no_content_header.visibility = GONE itemView.no_content_description.text = itemView.context.getString(R.string.synced_tabs_sign_in_message) itemView.no_content_button.text = itemView.context.getString(R.string.synced_tabs_sign_in_button) itemView.no_content_button.icon = ContextCompat.getDrawable(itemView.context, R.drawable.ic_sign_in) itemView.no_content_button.setOnClickListener { signInItem.navController.navigate(NavGraphDirections.actionGlobalTurnOnSync()) } } itemView.sync_tabs_error_description.text = itemView.context.getString(errorItem.descriptionResId) itemView.sync_tabs_error_cta_button.visibility = GONE companion object { const val LAYOUT_ID = R.layout.no_content_message_with_action errorItem.navController?.let { navController -> itemView.sync_tabs_error_cta_button.visibility = VISIBLE itemView.sync_tabs_error_cta_button.setOnClickListener { navController.navigate(NavGraphDirections.actionGlobalTurnOnSync()) } } class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { override fun <T : AdapterItem> bind(item: T, interactor: (Tab) -> Unit) { val errorItem = item as AdapterItem.Error setErrorMargins() itemView.no_content_header.visibility = GONE itemView.no_content_description.text = itemView.context.getString(errorItem.errorResId) } companion object { const val LAYOUT_ID = R.layout.no_content_message const val LAYOUT_ID = R.layout.sync_tabs_error_row } } Loading @@ -95,7 +80,12 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item } itemView.synced_tabs_group_name.text = device.device.displayName itemView.synced_tabs_group_name.setCompoundDrawablesWithIntrinsicBounds(deviceLogoDrawable, 0, 0, 0) itemView.synced_tabs_group_name.setCompoundDrawablesWithIntrinsicBounds( deviceLogoDrawable, 0, 0, 0 ) } companion object { Loading app/src/main/res/layout/sync_tabs_error_row.xml 0 → 100644 +31 −0 Original line number Diff line number Diff line <?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" xmlns:tools="http://schemas.android.com/tools" android:background="@drawable/empty_session_control_background" android:layout_marginBottom="12dp" android:padding="16dp" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/sync_tabs_error_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textSize="14sp" android:textAlignment="viewStart" tools:text="@string/synced_tabs_no_tabs"/> <com.google.android.material.button.MaterialButton android:id="@+id/sync_tabs_error_cta_button" style="@style/PositiveButton" app:icon="@drawable/ic_sign_in" android:visibility="gone" android:text="@string/synced_tabs_sign_in_button" android:layout_marginTop="8dp"/> </LinearLayout> app/src/main/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1464,4 +1464,6 @@ <string name="preferences_show_search_shortcuts">Show search shortcuts</string> <!-- DEPRECATED: Button in the search view that lets a user search by using a shortcut --> <string name="search_engines_shortcut_button">Search Engine</string> <!-- DEPRECATED: Text displayed when user is not logged into a Firefox Account --> <string name="synced_tabs_connect_to_sync_account">Connect with a Firefox Account.</string> </resources> Loading
app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt +8 −9 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ import androidx.recyclerview.widget.ListAdapter import mozilla.components.browser.storage.sync.SyncedDeviceTabs import org.mozilla.fenix.sync.SyncedTabsViewHolder.DeviceViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.ErrorViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.SignInViewHolder import org.mozilla.fenix.sync.SyncedTabsViewHolder.TabViewHolder import mozilla.components.browser.storage.sync.Tab as SyncTab import mozilla.components.concept.sync.Device as SyncDevice Loading @@ -28,7 +27,6 @@ class SyncedTabsAdapter( DeviceViewHolder.LAYOUT_ID -> DeviceViewHolder(itemView) TabViewHolder.LAYOUT_ID -> TabViewHolder(itemView) ErrorViewHolder.LAYOUT_ID -> ErrorViewHolder(itemView) SignInViewHolder.LAYOUT_ID -> SignInViewHolder(itemView) else -> throw IllegalStateException() } } Loading @@ -41,7 +39,6 @@ class SyncedTabsAdapter( is AdapterItem.Device -> DeviceViewHolder.LAYOUT_ID is AdapterItem.Tab -> TabViewHolder.LAYOUT_ID is AdapterItem.Error -> ErrorViewHolder.LAYOUT_ID is AdapterItem.SignIn -> SignInViewHolder.LAYOUT_ID } fun updateData(syncedTabs: List<SyncedDeviceTabs>) { Loading @@ -62,7 +59,7 @@ class SyncedTabsAdapter( when (oldItem) { is AdapterItem.Device -> newItem is AdapterItem.Device && oldItem.device.id == newItem.device.id is AdapterItem.Tab, AdapterItem.Error, AdapterItem.SignIn -> is AdapterItem.Tab, is AdapterItem.Error -> oldItem == newItem } Loading @@ -74,7 +71,9 @@ class SyncedTabsAdapter( sealed class AdapterItem { data class Device(val device: SyncDevice) : AdapterItem() data class Tab(val tab: SyncTab) : AdapterItem() data class SignIn(val navController: NavController) : AdapterItem() data class Error(val errorResId: Int) : AdapterItem() data class Error( val descriptionResId: Int, val navController: NavController? = null ) : AdapterItem() } }
app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt +29 −11 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import android.util.AttributeSet import android.widget.FrameLayout import androidx.annotation.StringRes import androidx.fragment.app.findFragment import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.component_sync_tabs.view.* Loading @@ -19,6 +20,7 @@ import kotlinx.coroutines.launch import mozilla.components.browser.storage.sync.SyncedDeviceTabs import mozilla.components.feature.syncedtabs.view.SyncedTabsView import org.mozilla.fenix.R import java.lang.IllegalStateException class SyncedTabsLayout @JvmOverloads constructor( context: Context, Loading @@ -45,10 +47,17 @@ class SyncedTabsLayout @JvmOverloads constructor( // We may still be displaying a "loading" spinner, hide it. stopLoading() sync_tabs_status.text = context.getText(stringResourceForError(error)) val navController: NavController? = try { findFragment<SyncedTabsFragment>().findNavController() } catch (exception: IllegalStateException) { null } val descriptionResId = stringResourceForError(error) val errorItem = getErrorItem(navController, error, descriptionResId) synced_tabs_list.visibility = View.GONE sync_tabs_status.visibility = View.VISIBLE val errorList: List<SyncedTabsAdapter.AdapterItem> = listOf(errorItem) adapter.submitList(errorList) synced_tabs_pull_to_refresh.isEnabled = pullToRefreshEnableState(error) } Loading @@ -56,17 +65,11 @@ class SyncedTabsLayout @JvmOverloads constructor( override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) { coroutineScope.launch { synced_tabs_list.visibility = View.VISIBLE sync_tabs_status.visibility = View.GONE adapter.updateData(syncedTabs) } } override fun startLoading() { synced_tabs_list.visibility = View.VISIBLE sync_tabs_status.visibility = View.GONE synced_tabs_pull_to_refresh.isRefreshing = true } Loading @@ -80,7 +83,8 @@ class SyncedTabsLayout @JvmOverloads constructor( } companion object { internal fun pullToRefreshEnableState(error: SyncedTabsView.ErrorType) = when (error) { private fun pullToRefreshEnableState(error: SyncedTabsView.ErrorType) = when (error) { // Disable "pull-to-refresh" when we clearly can't sync tabs, and user needs to take an // action within the app. SyncedTabsView.ErrorType.SYNC_UNAVAILABLE, Loading @@ -93,12 +97,26 @@ class SyncedTabsLayout @JvmOverloads constructor( SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> true } internal fun stringResourceForError(error: SyncedTabsView.ErrorType) = when (error) { private fun stringResourceForError(error: SyncedTabsView.ErrorType) = when (error) { SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_sign_in_message SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs } private fun getErrorItem( navController: NavController?, error: SyncedTabsView.ErrorType, @StringRes stringResId: Int ): SyncedTabsAdapter.AdapterItem = when (error) { SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE, SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE, SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION, SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> SyncedTabsAdapter.AdapterItem .Error(descriptionResId = stringResId) SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> SyncedTabsAdapter.AdapterItem .Error(descriptionResId = stringResId, navController = navController) } } }
app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt +19 −29 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ package org.mozilla.fenix.sync import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.no_content_message_with_action.view.* import kotlinx.android.synthetic.main.sync_tabs_error_row.view.* import kotlinx.android.synthetic.main.sync_tabs_list_item.view.* import kotlinx.android.synthetic.main.view_synced_tabs_group.view.* import mozilla.components.browser.storage.sync.Tab Loading Loading @@ -44,41 +44,26 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item } } class SignInViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { override fun <T : AdapterItem> bind(item: T, interactor: (Tab) -> Unit) { val signInItem = item as AdapterItem.SignIn val errorItem = item as AdapterItem.Error setErrorMargins() itemView.no_content_header.visibility = GONE itemView.no_content_description.text = itemView.context.getString(R.string.synced_tabs_sign_in_message) itemView.no_content_button.text = itemView.context.getString(R.string.synced_tabs_sign_in_button) itemView.no_content_button.icon = ContextCompat.getDrawable(itemView.context, R.drawable.ic_sign_in) itemView.no_content_button.setOnClickListener { signInItem.navController.navigate(NavGraphDirections.actionGlobalTurnOnSync()) } } itemView.sync_tabs_error_description.text = itemView.context.getString(errorItem.descriptionResId) itemView.sync_tabs_error_cta_button.visibility = GONE companion object { const val LAYOUT_ID = R.layout.no_content_message_with_action errorItem.navController?.let { navController -> itemView.sync_tabs_error_cta_button.visibility = VISIBLE itemView.sync_tabs_error_cta_button.setOnClickListener { navController.navigate(NavGraphDirections.actionGlobalTurnOnSync()) } } class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) { override fun <T : AdapterItem> bind(item: T, interactor: (Tab) -> Unit) { val errorItem = item as AdapterItem.Error setErrorMargins() itemView.no_content_header.visibility = GONE itemView.no_content_description.text = itemView.context.getString(errorItem.errorResId) } companion object { const val LAYOUT_ID = R.layout.no_content_message const val LAYOUT_ID = R.layout.sync_tabs_error_row } } Loading @@ -95,7 +80,12 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item } itemView.synced_tabs_group_name.text = device.device.displayName itemView.synced_tabs_group_name.setCompoundDrawablesWithIntrinsicBounds(deviceLogoDrawable, 0, 0, 0) itemView.synced_tabs_group_name.setCompoundDrawablesWithIntrinsicBounds( deviceLogoDrawable, 0, 0, 0 ) } companion object { Loading
app/src/main/res/layout/sync_tabs_error_row.xml 0 → 100644 +31 −0 Original line number Diff line number Diff line <?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" xmlns:tools="http://schemas.android.com/tools" android:background="@drawable/empty_session_control_background" android:layout_marginBottom="12dp" android:padding="16dp" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/sync_tabs_error_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textSize="14sp" android:textAlignment="viewStart" tools:text="@string/synced_tabs_no_tabs"/> <com.google.android.material.button.MaterialButton android:id="@+id/sync_tabs_error_cta_button" style="@style/PositiveButton" app:icon="@drawable/ic_sign_in" android:visibility="gone" android:text="@string/synced_tabs_sign_in_button" android:layout_marginTop="8dp"/> </LinearLayout>
app/src/main/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1464,4 +1464,6 @@ <string name="preferences_show_search_shortcuts">Show search shortcuts</string> <!-- DEPRECATED: Button in the search view that lets a user search by using a shortcut --> <string name="search_engines_shortcut_button">Search Engine</string> <!-- DEPRECATED: Text displayed when user is not logged into a Firefox Account --> <string name="synced_tabs_connect_to_sync_account">Connect with a Firefox Account.</string> </resources>