BrowserFragment.kt 11.4 KB
Newer Older
1
2
3
4
5
6
/* 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.samples.browser

7
import android.content.Intent
8
9
10
11
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
12
import androidx.fragment.app.Fragment
13
import kotlinx.android.synthetic.main.fragment_browser.view.*
14
15
import mozilla.components.browser.session.SelectionAwareSessionObserver
import mozilla.components.browser.session.Session
16
import mozilla.components.feature.app.links.AppLinksFeature
17
import mozilla.components.feature.awesomebar.AwesomeBarFeature
18
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
19
20
import mozilla.components.feature.contextmenu.ContextMenuCandidate
import mozilla.components.feature.contextmenu.ContextMenuFeature
21
import mozilla.components.feature.customtabs.CustomTabsToolbarFeature
22
import mozilla.components.feature.downloads.DownloadsFeature
23
import mozilla.components.feature.prompts.PromptFeature
24
import mozilla.components.feature.session.CoordinateScrollingFeature
25
import mozilla.components.feature.session.SessionFeature
Tiger Oakes's avatar
Tiger Oakes committed
26
import mozilla.components.feature.session.SwipeRefreshFeature
27
import mozilla.components.feature.session.ThumbnailsFeature
28
import mozilla.components.feature.session.WindowFeature
29
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
30
import mozilla.components.feature.tabs.toolbar.TabsToolbarFeature
31
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
32
import mozilla.components.feature.toolbar.ToolbarFeature
33
import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient
34
import mozilla.components.support.base.feature.BackHandler
35
import mozilla.components.support.base.feature.LifecycleAwareFeature
36
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
37
import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
38
import org.mozilla.samples.browser.ext.components
39
import org.mozilla.samples.browser.integration.FindInPageIntegration
40
import org.mozilla.samples.browser.integration.ReaderViewIntegration
41

42
class BrowserFragment : Fragment(), BackHandler {
43
44
45
46
47
48
    private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
    private val toolbarFeature = ViewBoundFeatureWrapper<ToolbarFeature>()
    private val customTabsToolbarFeature = ViewBoundFeatureWrapper<CustomTabsToolbarFeature>()
    private val downloadsFeature = ViewBoundFeatureWrapper<DownloadsFeature>()
    private val promptFeature = ViewBoundFeatureWrapper<PromptFeature>()
    private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
49
    private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>()
50
    private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
51
    private val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewIntegration>()
Tiger Oakes's avatar
Tiger Oakes committed
52
    private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>()
53
    private val appLinksFeature = ViewBoundFeatureWrapper<AppLinksFeature>()
54

55
    @Suppress("LongMethod")
56
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
57
        val layout = inflater.inflate(R.layout.fragment_browser, container, false)
58

59
        layout.toolbar.setMenuBuilder(components.menuBuilder)
60
61
62

        val sessionId = arguments?.getString(SESSION_ID)

63
64
        sessionFeature.set(
            feature = SessionFeature(
65
66
                components.sessionManager,
                components.sessionUseCases,
67
                layout.engineView,
68
69
70
                sessionId),
            owner = this,
            view = layout)
71

72
73
        toolbarFeature.set(
            feature = ToolbarFeature(
74
                layout.toolbar,
75
76
77
                components.sessionManager,
                components.sessionUseCases.loadUrl,
                components.defaultSearchUseCase,
78
79
80
                sessionId),
            owner = this,
            view = layout)
81

Tiger Oakes's avatar
Tiger Oakes committed
82
83
84
85
86
87
88
89
        swipeRefreshFeature.set(
            feature = SwipeRefreshFeature(
                components.sessionManager,
                components.sessionUseCases.reload,
                layout.swipeToRefresh),
            owner = this,
            view = layout)

90
91
        val menuUpdaterFeature = object : SelectionAwareSessionObserver(components.sessionManager),
                LifecycleAwareFeature {
92
93
94
95
96
97
98
            override fun onLoadingStateChanged(session: Session, loading: Boolean) {
                layout.toolbar.invalidateActions()
            }

            override fun onNavigationStateChanged(session: Session, canGoBack: Boolean, canGoForward: Boolean) {
                layout.toolbar.invalidateActions()
            }
99
100
101
102

            override fun start() {
                observeIdOrSelected(sessionId)
            }
103
104
        }

105
106
107
        ToolbarAutocompleteFeature(layout.toolbar).apply {
            addHistoryStorageProvider(components.historyStorage)
            addDomainProvider(components.shippedDomainsProvider)
108
109
        }

110
        TabsToolbarFeature(layout.toolbar, components.sessionManager, sessionId, ::showTabs)
111

112
113
114
115
        AwesomeBarFeature(layout.awesomeBar, layout.toolbar, layout.engineView, components.icons)
            .addHistoryProvider(
                components.historyStorage,
                components.sessionUseCases.loadUrl)
116
117
118
            .addSessionProvider(components.sessionManager, components.tabsUseCases.selectTab)
            .addSearchProvider(
                components.searchEngineManager.getDefaultSearchEngine(requireContext()),
119
                components.searchUseCases.defaultSearch,
120
121
                fetchClient = HttpURLConnectionClient(),
                mode = SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS)
122
            .addClipboardProvider(requireContext(), components.sessionUseCases.loadUrl)
123

124
125
126
127
128
129
130
131
132
133
        downloadsFeature.set(
            feature = DownloadsFeature(
                requireContext(),
                sessionManager = components.sessionManager,
                fragmentManager = childFragmentManager,
                onNeedToRequestPermissions = { permissions ->
                    requestPermissions(permissions, REQUEST_CODE_DOWNLOAD_PERMISSIONS)
                }),
            owner = this,
            view = layout)
134

135
        val scrollFeature = CoordinateScrollingFeature(components.sessionManager, layout.engineView, layout.toolbar)
136

137
        val contextMenuFeature = ContextMenuFeature(
138
139
140
141
142
            requireFragmentManager(),
            components.sessionManager,
            ContextMenuCandidate.defaultCandidates(
                requireContext(),
                components.tabsUseCases,
143
144
                layout),
            layout.engineView)
145

146
147
148
149
        promptFeature.set(
            feature = PromptFeature(
                fragment = this,
                sessionManager = components.sessionManager,
150
                sessionId = sessionId,
151
152
153
154
155
156
157
158
159
                fragmentManager = requireFragmentManager(),
                onNeedToRequestPermissions = { permissions ->
                    requestPermissions(permissions, REQUEST_CODE_PROMPT_PERMISSIONS)
                }),
            owner = this,
            view = layout)

        val windowFeature = WindowFeature(components.engine, components.sessionManager)

160
161
        sitePermissionsFeature.set(
            feature = SitePermissionsFeature(
162
163
                context = requireContext(),
                sessionManager = components.sessionManager,
164
                sessionId = sessionId,
165
                fragmentManager = requireFragmentManager()
166
167
168
169
170
171
172
            ) { permissions ->
                requestPermissions(permissions, REQUEST_CODE_APP_PERMISSIONS)
            },
            owner = this,
            view = layout
        )

173
174
175
176
177
178
179
180
181
        customTabsToolbarFeature.set(
            feature = CustomTabsToolbarFeature(
                components.sessionManager,
                layout.toolbar,
                sessionId,
                components.menuBuilder,
                closeListener = { activity?.finish() }),
            owner = this,
            view = layout)
182

183
        findInPageIntegration.set(
184
            feature = FindInPageIntegration(components.sessionManager, layout.findInPage, layout.engineView),
185
186
            owner = this,
            view = layout)
187

188
        readerViewFeature.set(
189
190
191
192
            feature = ReaderViewIntegration(
                requireContext(),
                components.engine,
                components.sessionManager,
193
194
195
                layout.toolbar,
                layout.readerViewBar,
                layout.readerViewAppearanceButton
196
            ),
197
198
199
200
            owner = this,
            view = layout
        )

201
202
203
204
        // Observe the lifecycle for supported features
        lifecycle.addObservers(
            scrollFeature,
            contextMenuFeature,
205
206
            windowFeature,
            menuUpdaterFeature
207
        )
208

209
210
211
212
213
214
        thumbnailsFeature.set(
            feature = ThumbnailsFeature(requireContext(), layout.engineView, components.sessionManager),
            owner = this,
            view = layout
        )

215
216
217
218
219
220
221
222
223
224
225
        appLinksFeature.set(
            feature = AppLinksFeature(
                context = requireContext(),
                sessionManager = components.sessionManager,
                sessionId = sessionId,
                fragmentManager = requireFragmentManager()
            ),
            owner = this,
            view = layout
        )

226
        return layout
227
228
229
230
231
232
233
234
235
    }

    private fun showTabs() {
        // For now we are performing manual fragment transactions here. Once we can use the new
        // navigation support library we may want to pass navigation graphs around.
        activity?.supportFragmentManager?.beginTransaction()?.apply {
            replace(R.id.container, TabsTrayFragment())
            commit()
        }
236
237
    }

238
239
    override fun onBackPressed(): Boolean {
        return when {
240
            readerViewFeature.onBackPressed() -> true
241
            findInPageIntegration.onBackPressed() -> true
242
243
            toolbarFeature.onBackPressed() -> true
            sessionFeature.onBackPressed() -> true
244
245
246
            customTabsToolbarFeature.onBackPressed() -> true
            else -> false
        }
247
248
249
250
    }

    companion object {
        private const val SESSION_ID = "session_id"
251
        private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1
252
        private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2
253
        private const val REQUEST_CODE_APP_PERMISSIONS = 3
254
255
256
257
258
259
260

        fun create(sessionId: String? = null): BrowserFragment = BrowserFragment().apply {
            arguments = Bundle().apply {
                putString(SESSION_ID, sessionId)
            }
        }
    }
261
262
263

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
264
265
266
267
268
269
            REQUEST_CODE_DOWNLOAD_PERMISSIONS -> downloadsFeature.withFeature {
                it.onPermissionsResult(permissions, grantResults)
            }
            REQUEST_CODE_PROMPT_PERMISSIONS -> promptFeature.withFeature {
                it.onPermissionsResult(permissions, grantResults)
            }
270
            REQUEST_CODE_APP_PERMISSIONS -> sitePermissionsFeature.withFeature {
271
                it.onPermissionsResult(permissions, grantResults)
272
            }
273
        }
274
275
276
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
277
        promptFeature.withFeature { it.onActivityResult(requestCode, resultCode, data) }
278
    }
279
}