[fenix] Issue https://github.com/mozilla-mobile/fenix/issues/19112: Remove old Synced Tabs code
parent
aa8d5795b2
commit
57df4f3fcc
@ -1,103 +0,0 @@
|
||||
/* 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.sync
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.component_sync_tabs.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_synced_tabs.*
|
||||
import kotlinx.android.synthetic.main.sync_tabs_error_row.view.*
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import mozilla.components.feature.syncedtabs.SyncedTabsFeature
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.library.LibraryPageFragment
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
|
||||
class SyncedTabsFragment : LibraryPageFragment<Tab>() {
|
||||
private val syncedTabsFeature = ViewBoundFeatureWrapper<SyncedTabsFeature>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_synced_tabs, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val backgroundServices = requireContext().components.backgroundServices
|
||||
|
||||
/*
|
||||
* Needed because the synced tabs error layout is also used in tabs tray where there is no private theme.
|
||||
* See https://github.com/mozilla-mobile/fenix/issues/15061
|
||||
*/
|
||||
setProperErrorColor(view.synced_tabs_list as RecyclerView)
|
||||
|
||||
syncedTabsFeature.set(
|
||||
feature = SyncedTabsFeature(
|
||||
context = requireContext(),
|
||||
storage = backgroundServices.syncedTabsStorage,
|
||||
accountManager = backgroundServices.accountManager,
|
||||
view = synced_tabs_layout,
|
||||
lifecycleOwner = this.viewLifecycleOwner,
|
||||
onTabClicked = ::handleTabClicked
|
||||
),
|
||||
owner = this,
|
||||
view = view
|
||||
)
|
||||
}
|
||||
|
||||
private fun setProperErrorColor(syncedTabsList: RecyclerView) {
|
||||
syncedTabsList.addOnChildAttachStateChangeListener(
|
||||
object : RecyclerView.OnChildAttachStateChangeListener {
|
||||
override fun onChildViewAttachedToWindow(view: View) {
|
||||
val errorView = view.sync_tabs_error_description
|
||||
val primaryText = ContextCompat.getColor(
|
||||
view.context,
|
||||
ThemeManager.resolveAttribute(R.attr.primaryText, view.context)
|
||||
)
|
||||
errorView?.let {
|
||||
it.setTextColor(primaryText)
|
||||
syncedTabsList.removeOnChildAttachStateChangeListener(
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChildViewDetachedFromWindow(view: View) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
showToolbar(getString(R.string.library_synced_tabs))
|
||||
}
|
||||
|
||||
private fun handleTabClicked(tab: Tab) {
|
||||
|
||||
(activity as HomeActivity).openToBrowserAndLoad(
|
||||
searchTermOrURL = tab.active().url,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromSyncedTabs
|
||||
)
|
||||
}
|
||||
|
||||
override val selectedItems: Set<Tab>
|
||||
get() = emptySet()
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
/* 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.sync
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
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.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.sync.ext.toAdapterItem
|
||||
import org.mozilla.fenix.sync.ext.toStringRes
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
class SyncedTabsLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr), SyncedTabsView {
|
||||
|
||||
override var listener: SyncedTabsView.Listener? = null
|
||||
private val metrics = context.components.analytics.metrics
|
||||
|
||||
private val adapter = SyncedTabsAdapter(ListenerDelegate(metrics) { listener })
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
init {
|
||||
inflate(getContext(), R.layout.component_sync_tabs, this)
|
||||
|
||||
synced_tabs_list.layoutManager = LinearLayoutManager(context)
|
||||
synced_tabs_list.adapter = adapter
|
||||
|
||||
synced_tabs_pull_to_refresh.setOnRefreshListener { listener?.onRefresh() }
|
||||
}
|
||||
|
||||
override fun onError(error: SyncedTabsView.ErrorType) {
|
||||
coroutineScope.launch {
|
||||
// We may still be displaying a "loading" spinner, hide it.
|
||||
stopLoading()
|
||||
|
||||
val navController: NavController? = try {
|
||||
findFragment<SyncedTabsFragment>().findNavController()
|
||||
} catch (exception: IllegalStateException) {
|
||||
null
|
||||
}
|
||||
|
||||
val descriptionResId = error.toStringRes()
|
||||
val errorItem = error.toAdapterItem(descriptionResId, navController)
|
||||
|
||||
val errorList: List<SyncedTabsAdapter.AdapterItem> = listOf(errorItem)
|
||||
adapter.submitList(errorList)
|
||||
|
||||
synced_tabs_pull_to_refresh.isEnabled = pullToRefreshEnableState(error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) {
|
||||
coroutineScope.launch {
|
||||
adapter.updateData(syncedTabs)
|
||||
}
|
||||
}
|
||||
|
||||
override fun startLoading() {
|
||||
synced_tabs_pull_to_refresh.isRefreshing = true
|
||||
}
|
||||
|
||||
override fun stopLoading() {
|
||||
synced_tabs_pull_to_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
coroutineScope.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal 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,
|
||||
SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> false
|
||||
|
||||
// Enable "pull-to-refresh" when an external event (e.g. connecting a desktop client,
|
||||
// or enabling tabs sync, or connecting to a network) may resolve our problem.
|
||||
SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE,
|
||||
SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE,
|
||||
SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to do this weird daisy-chaining of callbacks because the listener is nullable and
|
||||
* when we get a null reference, we never get a new binding to the non-null listener.
|
||||
*/
|
||||
class ListenerDelegate(
|
||||
private val metrics: MetricController,
|
||||
private val listener: (() -> SyncedTabsView.Listener?)
|
||||
) : SyncedTabsView.Listener {
|
||||
override fun onRefresh() {
|
||||
listener.invoke()?.onRefresh()
|
||||
}
|
||||
|
||||
override fun onTabClicked(tab: Tab) {
|
||||
listener.invoke()?.onTabClicked(tab)
|
||||
metrics.track(Event.SyncedTabOpened)
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?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.constraintlayout.widget.ConstraintLayout
|
||||
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:id="@+id/synced_tabs_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ProgressBar
|
||||
android:id="@+id/sync_tabs_progress_bar"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:indeterminate="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:translationY="-3dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/synced_tabs_pull_to_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/synced_tabs_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/sync_tabs_list_item"/>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,9 +0,0 @@
|
||||
<?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/. -->
|
||||
<org.mozilla.fenix.sync.SyncedTabsLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/synced_tabs_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,31 +0,0 @@
|
||||
/* 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.sync
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class ListenerDelegateTest {
|
||||
@Test
|
||||
fun `delegate invokes nullable listener`() {
|
||||
val listener: SyncedTabsView.Listener? = mockk(relaxed = true)
|
||||
val metrics: MetricController = mockk(relaxed = true)
|
||||
|
||||
val delegate = ListenerDelegate(metrics) { listener }
|
||||
|
||||
delegate.onRefresh()
|
||||
|
||||
verify { listener?.onRefresh() }
|
||||
|
||||
delegate.onTabClicked(mockk())
|
||||
|
||||
verify { listener?.onTabClicked(any()) }
|
||||
verify { metrics.track(Event.SyncedTabOpened) }
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/* 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.sync
|
||||
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class SyncedTabsLayoutTest {
|
||||
|
||||
@Test
|
||||
fun `pull to refresh state`() {
|
||||
assertTrue(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.MULTIPLE_DEVICES_UNAVAILABLE))
|
||||
assertTrue(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.SYNC_ENGINE_UNAVAILABLE))
|
||||
assertTrue(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.NO_TABS_AVAILABLE))
|
||||
assertFalse(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.SYNC_NEEDS_REAUTHENTICATION))
|
||||
assertFalse(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.SYNC_UNAVAILABLE))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue