SessionControlController.kt 11.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
/* 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.home.sessioncontrol

import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.browser.session.SessionManager
12
import mozilla.components.concept.engine.Engine
13
14
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.feature.tab.collections.TabCollection
15
import mozilla.components.feature.tab.collections.ext.restore
16
import mozilla.components.feature.tabs.TabsUseCases
17
import mozilla.components.feature.top.sites.TopSite
18
19
20
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
21
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
22
23
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.TabCollectionStorage
24
import org.mozilla.fenix.components.TopSiteStorage
25
26
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
27
import org.mozilla.fenix.components.tips.Tip
28
import org.mozilla.fenix.ext.nav
29
import org.mozilla.fenix.ext.sessionsOfType
30
31
32
33
34
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.settings.SupportUtils
ekager's avatar
ekager committed
35
import mozilla.components.feature.tab.collections.Tab as ComponentTab
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

/**
 * [HomeFragment] controller. An interface that handles the view manipulation of the Tabs triggered
 * by the Interactor.
 */
@SuppressWarnings("TooManyFunctions")
interface SessionControlController {
    /**
     * @see [CollectionInteractor.onCollectionAddTabTapped]
     */
    fun handleCollectionAddTabTapped(collection: TabCollection)

    /**
     * @see [CollectionInteractor.onCollectionOpenTabClicked]
     */
    fun handleCollectionOpenTabClicked(tab: ComponentTab)

    /**
     * @see [CollectionInteractor.onCollectionOpenTabsTapped]
     */
    fun handleCollectionOpenTabsTapped(collection: TabCollection)

    /**
     * @see [CollectionInteractor.onCollectionRemoveTab]
     */
    fun handleCollectionRemoveTab(collection: TabCollection, tab: ComponentTab)

    /**
     * @see [CollectionInteractor.onCollectionShareTabsClicked]
     */
    fun handleCollectionShareTabsClicked(collection: TabCollection)

    /**
     * @see [CollectionInteractor.onDeleteCollectionTapped]
     */
    fun handleDeleteCollectionTapped(collection: TabCollection)

73
74
75
76
77
    /**
     * @see [TopSiteInteractor.onOpenInPrivateTabClicked]
     */
    fun handleOpenInPrivateTabClicked(topSite: TopSite)

78
79
80
81
82
    /**
     * @see [TabSessionInteractor.onPrivateBrowsingLearnMoreClicked]
     */
    fun handlePrivateBrowsingLearnMoreClicked()

83
84
85
86
87
    /**
     * @see [TopSiteInteractor.onRemoveTopSiteClicked]
     */
    fun handleRemoveTopSiteClicked(topSite: TopSite)

88
89
90
91
92
    /**
     * @see [CollectionInteractor.onRenameCollectionTapped]
     */
    fun handleRenameCollectionTapped(collection: TabCollection)

93
94
95
    /**
     * @see [TopSiteInteractor.onSelectTopSite]
     */
96
    fun handleSelectTopSite(url: String, isDefault: Boolean)
97

98
99
100
101
102
    /**
     * @see [OnboardingInteractor.onStartBrowsingClicked]
     */
    fun handleStartBrowsingClicked()

103
104
105
106
107
    /**
     * @see [OnboardingInteractor.onOpenSettingsClicked]
     */
    fun handleOpenSettingsClicked()

108
109
110
111
112
113
114
115
116
117
    /**
     * @see [OnboardingInteractor.onWhatsNewGetAnswersClicked]
     */
    fun handleWhatsNewGetAnswersClicked()

    /**
     * @see [OnboardingInteractor.onReadPrivacyNoticeClicked]
     */
    fun handleReadPrivacyNoticeClicked()

118
119
120
121
    /**
     * @see [CollectionInteractor.onToggleCollectionExpanded]
     */
    fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean)
122

123
    fun handleCloseTip(tip: Tip)
124
125
126
127
128

    /**
     * @see [CollectionInteractor.onAddTabsToCollectionTapped]
     */
    fun handleCreateCollection()
129
130
}

131
@SuppressWarnings("TooManyFunctions", "LargeClass")
132
133
class DefaultSessionControlController(
    private val activity: HomeActivity,
134
135
136
137
138
139
    private val engine: Engine,
    private val metrics: MetricController,
    private val sessionManager: SessionManager,
    private val tabCollectionStorage: TabCollectionStorage,
    private val topSiteStorage: TopSiteStorage,
    private val addTabUseCase: TabsUseCases.AddNewTabUseCase,
140
    private val fragmentStore: HomeFragmentStore,
141
    private val navController: NavController,
142
    private val viewLifecycleScope: CoroutineScope,
143
144
    private val hideOnboarding: () -> Unit,
    private val registerCollectionStorageObserver: () -> Unit,
145
    private val showDeleteCollectionPrompt: (tabCollection: TabCollection, title: String?, message: String) -> Unit,
146
    private val showTabTray: () -> Unit
147
148
149
150
151
152
153
154
155
156
157
) : SessionControlController {

    override fun handleCollectionAddTabTapped(collection: TabCollection) {
        metrics.track(Event.CollectionAddTabPressed)
        showCollectionCreationFragment(
            step = SaveCollectionStep.SelectTabs,
            selectedTabCollectionId = collection.id
        )
    }

    override fun handleCollectionOpenTabClicked(tab: ComponentTab) {
158
159
        sessionManager.restore(
            activity,
160
            engine,
161
162
163
164
165
166
167
168
169
170
171
            tab,
            onTabRestored = {
                activity.openToBrowser(BrowserDirection.FromHome)
            },
            onFailure = {
                activity.openToBrowserAndLoad(
                    searchTermOrURL = tab.url,
                    newTab = true,
                    from = BrowserDirection.FromHome
                )
            }
172
173
174
175
176
177
        )

        metrics.track(Event.CollectionTabRestored)
    }

    override fun handleCollectionOpenTabsTapped(collection: TabCollection) {
178
179
        sessionManager.restore(
            activity,
180
            engine,
181
182
            collection,
            onFailure = { url ->
183
                addTabUseCase.invoke(url)
184
            }
185
        )
186

187
        showTabTray()
188
189
190
191
192
193
        metrics.track(Event.CollectionAllTabsRestored)
    }

    override fun handleCollectionRemoveTab(collection: TabCollection, tab: ComponentTab) {
        metrics.track(Event.CollectionTabRemoved)

194
195
196
197
198
199
200
201
        if (collection.tabs.size == 1) {
            val title = activity.resources.getString(R.string.delete_tab_and_collection_dialog_title, collection.title)
            val message = activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
            showDeleteCollectionPrompt(collection, title, message)
        } else {
            viewLifecycleScope.launch(Dispatchers.IO) {
                tabCollectionStorage.removeTabFromCollection(collection, tab)
            }
202
203
204
205
206
207
208
209
210
        }
    }

    override fun handleCollectionShareTabsClicked(collection: TabCollection) {
        showShareFragment(collection.tabs.map { ShareData(url = it.url, title = it.title) })
        metrics.track(Event.CollectionShared)
    }

    override fun handleDeleteCollectionTapped(collection: TabCollection) {
211
212
        val message = activity.resources.getString(R.string.tab_collection_dialog_message, collection.title)
        showDeleteCollectionPrompt(collection, null, message)
213
214
    }

215
    override fun handleOpenInPrivateTabClicked(topSite: TopSite) {
216
        metrics.track(Event.TopSiteOpenInPrivateTab)
217
218
219
220
221
222
223
224
225
226
        with(activity) {
            browsingModeManager.mode = BrowsingMode.Private
            openToBrowserAndLoad(
                searchTermOrURL = topSite.url,
                newTab = true,
                from = BrowserDirection.FromHome
            )
        }
    }

227
228
229
230
231
232
233
234
235
    override fun handlePrivateBrowsingLearnMoreClicked() {
        activity.openToBrowserAndLoad(
            searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
                (SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS),
            newTab = true,
            from = BrowserDirection.FromHome
        )
    }

236
    override fun handleRemoveTopSiteClicked(topSite: TopSite) {
237
        metrics.track(Event.TopSiteRemoved)
ekager's avatar
ekager committed
238
239
240
        if (topSite.url == SupportUtils.POCKET_TRENDING_URL) {
            metrics.track(Event.PocketTopSiteRemoved)
        }
241

242
        viewLifecycleScope.launch(Dispatchers.IO) {
243
244
245
246
            topSiteStorage.removeTopSite(topSite)
        }
    }

247
248
249
250
251
252
253
254
    override fun handleRenameCollectionTapped(collection: TabCollection) {
        showCollectionCreationFragment(
            step = SaveCollectionStep.RenameCollection,
            selectedTabCollectionId = collection.id
        )
        metrics.track(Event.CollectionRenamePressed)
    }

255
    override fun handleSelectTopSite(url: String, isDefault: Boolean) {
256
        metrics.track(Event.TopSiteOpenInNewTab)
257
        if (isDefault) { metrics.track(Event.TopSiteOpenDefault) }
258
        if (url == SupportUtils.POCKET_TRENDING_URL) { metrics.track(Event.PocketTopSiteClicked) }
259
        addTabUseCase.invoke(
260
261
262
            url = url,
            selectTab = true,
            startLoading = true
263
        )
264
        activity.openToBrowser(BrowserDirection.FromHome)
265
266
    }

267
268
269
270
    override fun handleStartBrowsingClicked() {
        hideOnboarding()
    }

271
    override fun handleOpenSettingsClicked() {
272
273
        val directions = HomeFragmentDirections.actionGlobalPrivateBrowsingFragment()
        navController.nav(R.id.homeFragment, directions)
274
275
    }

276
    override fun handleWhatsNewGetAnswersClicked() {
277
278
279
280
281
        activity.openToBrowserAndLoad(
            searchTermOrURL = SupportUtils.getWhatsNewUrl(activity),
            newTab = true,
            from = BrowserDirection.FromHome
        )
282
283
284
    }

    override fun handleReadPrivacyNoticeClicked() {
285
286
287
288
289
        activity.openToBrowserAndLoad(
            searchTermOrURL = SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE),
            newTab = true,
            from = BrowserDirection.FromHome
        )
290
291
    }

292
    override fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
293
        fragmentStore.dispatch(HomeFragmentAction.CollectionExpanded(collection, expand))
294
295
    }

296
297
298
299
    override fun handleCloseTip(tip: Tip) {
        fragmentStore.dispatch(HomeFragmentAction.RemoveTip(tip))
    }

300
301
302
303
304
305
306
307
308
309
    private fun showCollectionCreationFragment(
        step: SaveCollectionStep,
        selectedTabIds: Array<String>? = null,
        selectedTabCollectionId: Long? = null
    ) {
        if (navController.currentDestination?.id == R.id.collectionCreationFragment) return

        // Only register the observer right before moving to collection creation
        registerCollectionStorageObserver()

310
311
312
313
314
        val tabIds = sessionManager
            .sessionsOfType(private = activity.browsingModeManager.mode.isPrivate)
            .map { session -> session.id }
            .toList()
            .toTypedArray()
315
        val directions = HomeFragmentDirections.actionGlobalCollectionCreationFragment(
316
317
318
319
320
321
322
323
            tabIds = tabIds,
            saveCollectionStep = step,
            selectedTabIds = selectedTabIds,
            selectedTabCollectionId = selectedTabCollectionId ?: -1
        )
        navController.nav(R.id.homeFragment, directions)
    }

324
325
326
327
    override fun handleCreateCollection() {
        showCollectionCreationFragment(step = SaveCollectionStep.SelectTabs)
    }

328
    private fun showShareFragment(data: List<ShareData>) {
Jeff Boek's avatar
Jeff Boek committed
329
        val directions = HomeFragmentDirections.actionGlobalShareFragment(
330
331
332
333
334
            data = data.toTypedArray()
        )
        navController.nav(R.id.homeFragment, directions)
    }
}