Unverified Commit a899f121 authored by Jonathan Almeida's avatar Jonathan Almeida
Browse files

Close #6836: Refactor feature-syncedtabs to use interactor-presenter pattern

parent e7ff11cc
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
/* 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 mozilla.components.feature.syncedtabs

import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.Dispatchers
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.controller.DefaultController
import mozilla.components.feature.syncedtabs.controller.SyncedTabsController
import mozilla.components.feature.syncedtabs.interactor.DefaultInteractor
import mozilla.components.feature.syncedtabs.interactor.SyncedTabsInteractor
import mozilla.components.feature.syncedtabs.presenter.DefaultPresenter
import mozilla.components.feature.syncedtabs.presenter.SyncedTabsPresenter
import mozilla.components.feature.syncedtabs.storage.SyncedTabsStorage
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.support.base.feature.LifecycleAwareFeature
import kotlin.coroutines.CoroutineContext

/**
 * Feature implementation that will keep a [SyncedTabsView] notified with other synced device tabs for
 * the Firefox Sync account.
 *
 * @param storage The synced tabs storage that stores the current device's and remote device tabs.
 * @param accountManager Firefox Account Manager that holds a Firefox Sync account.
 * @param view An implementor of [SyncedTabsView] that will be notified of changes.
 * @param lifecycleOwner Android Lifecycle Owner to bind observers onto.
 * @param coroutineContext A coroutine context that can be used to perform work off the main thread.
 * @param onTabClicked Invoked when a tab is selected by the user on the [SyncedTabsView].
 * @param controller See [SyncedTabsController].
 * @param presenter See [SyncedTabsPresenter].
 * @param interactor See [SyncedTabsInteractor].
 */
class SyncedTabsFeature(
    storage: SyncedTabsStorage,
    accountManager: FxaAccountManager,
    view: SyncedTabsView,
    lifecycleOwner: LifecycleOwner,
    coroutineContext: CoroutineContext = Dispatchers.IO,
    onTabClicked: (Tab) -> Unit,
    controller: SyncedTabsController = DefaultController(
        storage,
        accountManager,
        view,
        coroutineContext
    ),
    private val presenter: SyncedTabsPresenter = DefaultPresenter(
        controller,
        accountManager,
        view,
        lifecycleOwner
    ),
    private val interactor: SyncedTabsInteractor = DefaultInteractor(
        accountManager,
        view,
        coroutineContext,
        onTabClicked
    )
) : LifecycleAwareFeature {

    override fun start() {
        presenter.start()
        interactor.start()
    }

    override fun stop() {
        presenter.stop()
        interactor.stop()
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -4,12 +4,12 @@

package mozilla.components.feature.syncedtabs

import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.storage.sync.TabEntry
import mozilla.components.concept.awesomebar.AwesomeBar
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.syncedtabs.storage.SyncedTabsStorage
import java.util.UUID

/**
+48 −0
Original line number Diff line number Diff line
/* 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 mozilla.components.feature.syncedtabs.controller

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.feature.syncedtabs.storage.SyncedTabsProvider
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.ext.withConstellation
import kotlin.coroutines.CoroutineContext

internal class DefaultController(
    override val provider: SyncedTabsProvider,
    override val accountManager: FxaAccountManager,
    override val view: SyncedTabsView,
    private val coroutineContext: CoroutineContext
) : SyncedTabsController {

    override fun syncTabs() {
        view.startLoading()

        val scope = CoroutineScope(coroutineContext)

        scope.launch {
            accountManager.withConstellation {
                val syncedTabs = provider.getSyncedTabs()
                val otherDevices = state()?.otherDevices

                scope.launch(Dispatchers.Main) {
                    if (syncedTabs.isEmpty() && otherDevices?.isEmpty() == true) {
                        view.onError(ErrorType.MULTIPLE_DEVICES_UNAVAILABLE)
                    } else {
                        view.displaySyncedTabs(syncedTabs)
                    }
                }
            }

            scope.launch(Dispatchers.Main) {
                view.stopLoading()
            }
        }
    }
}
+25 −0
Original line number Diff line number Diff line
/* 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 mozilla.components.feature.syncedtabs.controller

import mozilla.components.feature.syncedtabs.storage.SyncedTabsProvider
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager

/**
 * A controller for making the appropriate request for remote tabs from [SyncedTabsProvider] when the
 * [FxaAccountManager] account is in the appropriate state. The [SyncedTabsView] can then be notified.
 */
interface SyncedTabsController {
    val provider: SyncedTabsProvider
    val accountManager: FxaAccountManager
    val view: SyncedTabsView

    /**
     * Requests for remote tabs and notifies the [SyncedTabsView] when available with [SyncedTabsView.displaySyncedTabs]
     * otherwise notifies the appropriate error to [SyncedTabsView.onError].
     */
    fun syncTabs()
}
+43 −0
Original line number Diff line number Diff line
/* 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 mozilla.components.feature.syncedtabs.interactor

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.ext.withConstellation
import mozilla.components.service.fxa.sync.SyncReason
import kotlin.coroutines.CoroutineContext

internal class DefaultInteractor(
    override val accountManager: FxaAccountManager,
    override val view: SyncedTabsView,
    private val coroutineContext: CoroutineContext,
    override val tabClicked: (Tab) -> Unit
) : SyncedTabsInteractor {

    override fun start() {
        view.listener = this
    }

    override fun stop() {
        view.listener = null
    }

    override fun onTabClicked(tab: Tab) {
        tabClicked(tab)
    }

    override fun onRefresh() {
        CoroutineScope(coroutineContext).launch {
            accountManager.withConstellation {
                refreshDevicesAsync()
            }
            accountManager.syncNowAsync(SyncReason.User, true)
        }
    }
}
Loading