diff --git a/app/build.gradle b/app/build.gradle
index 3528113bbdf9..1de7e752aeb5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -353,6 +353,7 @@ dependencies {
implementation Deps.mozilla_feature_media
implementation Deps.mozilla_feature_prompts
implementation Deps.mozilla_feature_push
+ implementation Deps.mozilla_feature_pwa
implementation Deps.mozilla_feature_qr
implementation Deps.mozilla_feature_search
implementation Deps.mozilla_feature_session
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 47a22ca78bdc..84f79770468b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -61,7 +61,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
index 81e4d5569a57..5a8f2dd017b8 100644
--- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
@@ -233,6 +233,8 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
)
}
+ open fun getDirections(customTabSessionId: String?) = NavGraphDirections.actionGlobalBrowser(customTabSessionId)
+
private fun load(
searchTermOrURL: String,
newTab: Boolean,
diff --git a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt
index 90b1db780808..2086c286c4a7 100644
--- a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt
@@ -12,18 +12,23 @@ import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.customtabs.AuthCustomTabActivity
-import org.mozilla.fenix.customtabs.CustomTabActivity
+import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.utils.Settings
+/**
+ * Processes incoming intents and sends them to the corresponding activity.
+ */
class IntentReceiverActivity : Activity() {
// Holds the intent that initially started this activity
// so that it can persist through the speech activity.
private var previousIntent: Intent? = null
- @Suppress("ComplexMethod")
+ private fun tabIntentProcessor(isPrivate: Boolean) =
+ if (isPrivate) components.utils.privateIntentProcessor else components.utils.intentProcessor
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -40,17 +45,14 @@ class IntentReceiverActivity : Activity() {
// the HomeActivity.
val intent = intent?.let { Intent(intent) } ?: Intent()
- val intentProcessors = listOf(
- components.utils.customTabIntentProcessor,
- if (isPrivate) components.utils.privateIntentProcessor else components.utils.intentProcessor
- )
+ val intentProcessors = components.utils.externalAppIntentProcessors + tabIntentProcessor(isPrivate)
if (intent.getBooleanExtra(SPEECH_PROCESSING, false)) {
previousIntent = intent
displaySpeechRecognizer()
} else {
intentProcessors.any { it.process(intent) }
- setIntentActivity(intent)
+ setIntentActivity(intent, isPrivate)
startActivity(intent)
@@ -59,18 +61,21 @@ class IntentReceiverActivity : Activity() {
}
}
- private fun setIntentActivity(intent: Intent) {
+ /**
+ * Sets the activity that this [intent] will launch.
+ */
+ private fun setIntentActivity(intent: Intent, isPrivate: Boolean) {
val openToBrowser = when {
- components.utils.customTabIntentProcessor.matches(intent) -> {
+ components.utils.externalAppIntentProcessors.any { it.matches(intent) } -> {
val activityClass = if (intent.hasExtra(getString(R.string.intent_extra_auth))) {
AuthCustomTabActivity::class
} else {
- CustomTabActivity::class
+ ExternalAppBrowserActivity::class
}
intent.setClassName(applicationContext, activityClass.java.name)
true
}
- intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND -> {
+ tabIntentProcessor(isPrivate).matches(intent) -> {
intent.setClassName(applicationContext, HomeActivity::class.java.name)
// This Intent was launched from history (recent apps). Android will redeliver the
// original Intent (which might be a VIEW intent). However if there's no active browsing
diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
index 366791e0362a..14b7aafa5edc 100644
--- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
@@ -323,15 +323,7 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
toolbar.visibility = View.VISIBLE
nestedScrollQuickAction.visibility = View.VISIBLE
}
- view.swipeRefresh.apply {
- val (topMargin, bottomMargin) = if (inFullScreen) 0 to 0 else getEngineMargins()
- (layoutParams as CoordinatorLayout.LayoutParams).setMargins(
- 0,
- topMargin,
- 0,
- bottomMargin
- )
- }
+ updateLayoutMargins(inFullScreen)
},
owner = this,
view = view
@@ -488,6 +480,13 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
*/
protected abstract fun getAppropriateLayoutGravity(): Int
+ protected fun updateLayoutMargins(inFullScreen: Boolean) {
+ view?.swipeRefresh?.apply {
+ val (topMargin, bottomMargin) = if (inFullScreen) 0 to 0 else getEngineMargins()
+ (layoutParams as CoordinatorLayout.LayoutParams).setMargins(0, topMargin, 0, bottomMargin)
+ }
+ }
+
/**
* Updates the site permissions rules based on user settings.
*/
diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt
index 013f522253c7..585f4fc8d28b 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Components.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt
@@ -19,7 +19,9 @@ class Components(private val context: Context) {
val services by lazy { Services(backgroundServices.accountManager) }
val core by lazy { Core(context) }
val search by lazy { Search(context) }
- val useCases by lazy { UseCases(context, core.sessionManager, core.engine.settings, search.searchEngineManager) }
+ val useCases by lazy {
+ UseCases(context, core.sessionManager, core.engine.settings, search.searchEngineManager, core.client)
+ }
val utils by lazy { Utilities(context, core.sessionManager, useCases.sessionUseCases, useCases.searchUseCases) }
val analytics by lazy { Analytics(context) }
val publicSuffixList by lazy { PublicSuffixList(context) }
diff --git a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
index 2f018465bab2..93897c5f099c 100644
--- a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
@@ -8,7 +8,9 @@ import android.content.Context
import mozilla.components.browser.search.SearchEngineManager
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.Settings
+import mozilla.components.concept.fetch.Client
import mozilla.components.feature.app.links.AppLinksUseCases
+import mozilla.components.feature.pwa.WebAppUseCases
import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.SettingsUseCases
@@ -24,7 +26,8 @@ class UseCases(
private val context: Context,
private val sessionManager: SessionManager,
private val engineSettings: Settings,
- private val searchEngineManager: SearchEngineManager
+ private val searchEngineManager: SearchEngineManager,
+ private val httpClient: Client
) {
/**
* Use cases that provide engine interactions for a given browser session.
@@ -47,4 +50,6 @@ class UseCases(
val settingsUseCases by lazy { SettingsUseCases(engineSettings, sessionManager) }
val appLinksUseCases by lazy { AppLinksUseCases(context.applicationContext) }
+
+ val webAppUseCases by lazy { WebAppUseCases(context, sessionManager, httpClient) }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt
index 4f28afbeba91..8a54f294b226 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt
@@ -8,6 +8,8 @@ import android.content.Context
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.customtabs.CustomTabIntentProcessor
import mozilla.components.feature.intent.TabIntentProcessor
+import mozilla.components.feature.pwa.ManifestStorage
+import mozilla.components.feature.pwa.intent.WebAppIntentProcessor
import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionUseCases
import org.mozilla.fenix.test.Mockable
@@ -23,18 +25,23 @@ class Utilities(
private val searchUseCases: SearchUseCases
) {
/**
- * Provides intent processing functionality for CustomTab, ACTION_VIEW
- * and ACTION_SEND intents.
+ * Provides intent processing functionality for ACTION_VIEW and ACTION_SEND intents.
*/
val intentProcessor by lazy {
TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch, isPrivate = false)
}
+ /**
+ * Provides intent processing functionality for ACTION_VIEW and ACTION_SEND intents in private tabs.
+ */
val privateIntentProcessor by lazy {
TabIntentProcessor(sessionManager, sessionUseCases.loadUrl, searchUseCases.newTabSearch, isPrivate = true)
}
- val customTabIntentProcessor by lazy {
- CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources)
+ val externalAppIntentProcessors by lazy {
+ listOf(
+ WebAppIntentProcessor(sessionManager, sessionUseCases.loadUrl, ManifestStorage(context)),
+ CustomTabIntentProcessor(sessionManager, sessionUseCases.loadUrl, context.resources)
+ )
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt
index baa522b344b4..af3ae4009ac5 100644
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt
@@ -249,7 +249,7 @@ sealed class Event {
enum class Item {
SETTINGS, LIBRARY, HELP, DESKTOP_VIEW_ON, DESKTOP_VIEW_OFF, FIND_IN_PAGE, NEW_TAB,
NEW_PRIVATE_TAB, SHARE, REPORT_SITE_ISSUE, BACK, FORWARD, RELOAD, STOP, OPEN_IN_FENIX,
- SAVE_TO_COLLECTION
+ SAVE_TO_COLLECTION, ADD_TO_HOMESCREEN
}
override val extras: Map?
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
index 086fc40bb4de..d83e3d3dcd97 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
@@ -11,7 +11,9 @@ import androidx.core.widget.NestedScrollView
import androidx.navigation.NavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.MainScope
import kotlinx.coroutines.ObsoleteCoroutinesApi
+import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.concept.engine.EngineView
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
@@ -86,6 +88,11 @@ class DefaultBrowserToolbarController(
item.isChecked,
currentSession
)
+ ToolbarMenu.Item.AddToHomescreen -> {
+ MainScope().launch {
+ context.components.useCases.webAppUseCases.addToHomescreen()
+ }
+ }
ToolbarMenu.Item.Share -> {
val currentUrl = currentSession?.url
currentUrl?.apply {
@@ -175,6 +182,7 @@ class DefaultBrowserToolbarController(
ToolbarMenu.Item.OpenInFenix -> Event.BrowserMenuItemTapped.Item.OPEN_IN_FENIX
ToolbarMenu.Item.Share -> Event.BrowserMenuItemTapped.Item.SHARE
ToolbarMenu.Item.SaveToCollection -> Event.BrowserMenuItemTapped.Item.SAVE_TO_COLLECTION
+ ToolbarMenu.Item.AddToHomescreen -> Event.BrowserMenuItemTapped.Item.ADD_TO_HOMESCREEN
}
context.components.analytics.metrics.track(Event.BrowserMenuItemTapped(eventItem))
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt
index e4eccba7c972..6d891da46c22 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt
@@ -12,6 +12,7 @@ import mozilla.components.browser.menu.item.BrowserMenuImageText
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
import mozilla.components.browser.menu.item.BrowserMenuSwitch
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
+import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.theme.ThemeManager
@@ -133,10 +134,20 @@ class DefaultToolbarMenu(
onItemTapped.invoke(ToolbarMenu.Item.Library)
},
- BrowserMenuSwitch(context.getString(R.string.browser_menu_desktop_site),
- requestDesktopStateProvider, { checked ->
- onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
- }),
+ BrowserMenuSwitch(
+ context.getString(R.string.browser_menu_desktop_site),
+ requestDesktopStateProvider
+ ) { checked ->
+ onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
+ },
+
+ SimpleBrowserMenuItem(
+ context.getString(R.string.browser_menu_add_to_homescreen)
+ ) {
+ onItemTapped.invoke(ToolbarMenu.Item.AddToHomescreen)
+ }.apply {
+ visible = ::shouldShowAddToHomescreen
+ },
BrowserMenuImageText(
context.getString(R.string.browser_menu_find_in_page),
@@ -201,4 +212,9 @@ class DefaultToolbarMenu(
items
}
+
+ private fun shouldShowAddToHomescreen(): Boolean {
+ return context.components.useCases.webAppUseCases.isPinningSupported() &&
+ context.components.core.sessionManager.selectedSession != null
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
index 4db34ea05921..8a002635ee3b 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
@@ -24,6 +24,7 @@ interface ToolbarMenu {
object ReportIssue : Item()
object OpenInFenix : Item()
object SaveToCollection : Item()
+ object AddToHomescreen : Item()
}
val menuBuilder: BrowserMenuBuilder
diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt
index af762dcc7815..3f5be9900120 100644
--- a/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt
@@ -6,22 +6,25 @@ package org.mozilla.fenix.customtabs
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.OAuthAccount
-import mozilla.components.service.fxa.manager.FxaAccountManager
import org.mozilla.fenix.ext.components
-class AuthCustomTabActivity : CustomTabActivity() {
- private lateinit var accountManager: FxaAccountManager
+/**
+ * A special custom tab for signing into a Firefox Account. The activity is closed once the user is signed in.
+ */
+class AuthCustomTabActivity : ExternalAppBrowserActivity() {
- // Navigate away from this activity when we have successful authentication
private val accountStateObserver = object : AccountObserver {
+ /**
+ * Navigate away from this activity when we have successful authentication
+ */
override fun onAuthenticated(account: OAuthAccount, newAccount: Boolean) {
- this@AuthCustomTabActivity.finish()
+ finish()
}
}
override fun onResume() {
super.onResume()
- accountManager = this.components.backgroundServices.accountManager
+ val accountManager = this.components.backgroundServices.accountManager
accountManager.register(accountStateObserver, this, true)
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
similarity index 54%
rename from app/src/main/java/org/mozilla/fenix/customtabs/CustomTabActivity.kt
rename to app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
index 05ff3ea9a0ff..1c25e9a69b91 100644
--- a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
@@ -5,7 +5,11 @@
package org.mozilla.fenix.customtabs
import androidx.navigation.NavDestination
+import androidx.navigation.NavDirections
import mozilla.components.browser.session.intent.getSessionId
+import mozilla.components.concept.engine.manifest.WebAppManifestParser
+import mozilla.components.feature.pwa.ext.getTrustedScope
+import mozilla.components.feature.pwa.ext.getWebAppManifest
import mozilla.components.support.utils.SafeIntent
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
@@ -15,7 +19,12 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.theme.CustomTabThemeManager
import java.security.InvalidParameterException
-open class CustomTabActivity : HomeActivity() {
+/**
+ * Activity that holds the [BrowserFragment] that is launched within an external app,
+ * such as custom tabs and progressive web apps.
+ */
+open class ExternalAppBrowserActivity : HomeActivity() {
+
final override fun getSentryBreadcrumbMessage(destination: NavDestination): String {
val fragmentName = resources.getResourceEntryName(destination.id)
return "Changing to fragment $fragmentName, isCustomTab: true"
@@ -28,12 +37,23 @@ open class CustomTabActivity : HomeActivity() {
override fun getNavDirections(
from: BrowserDirection,
customTabSessionId: String?
- ) = when (from) {
- BrowserDirection.FromGlobal ->
- NavGraphDirections.actionGlobalExternalAppBrowser(customTabSessionId)
- else -> throw InvalidParameterException(
- "Tried to navigate to ExternalAppBrowserFragment from $from"
- )
+ ): NavDirections {
+ val manifest = intent.getWebAppManifest()
+ val manifestJson = manifest?.let { WebAppManifestParser().serialize(it).toString() }
+ val trustedScopes = listOfNotNull(
+ manifest?.getTrustedScope()?.toString()
+ ).toTypedArray()
+ return when (from) {
+ BrowserDirection.FromGlobal ->
+ NavGraphDirections.actionGlobalExternalAppBrowser(
+ activeSessionId = customTabSessionId,
+ webAppManifest = manifestJson,
+ trustedScopes = trustedScopes
+ )
+ else -> throw InvalidParameterException(
+ "Tried to navigate to ExternalAppBrowserFragment from $from"
+ )
+ }
}
final override fun createBrowsingModeManager() =
diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt
index b20c42139d5c..23ee858c9b17 100644
--- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt
@@ -6,11 +6,19 @@ package org.mozilla.fenix.customtabs
import android.view.Gravity
import android.view.View
+import androidx.core.net.toUri
+import androidx.core.view.isGone
+import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.component_search.*
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import mozilla.components.browser.session.Session
+import mozilla.components.concept.engine.manifest.WebAppManifestParser
+import mozilla.components.concept.engine.manifest.getOrNull
+import mozilla.components.feature.pwa.feature.WebAppActivityFeature
+import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature
+import mozilla.components.feature.pwa.feature.WebAppSiteControlsFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
@@ -29,7 +37,10 @@ import org.mozilla.fenix.ext.requireComponents
@ExperimentalCoroutinesApi
class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
+ private val args by navArgs()
+
private val customTabsIntegration = ViewBoundFeatureWrapper()
+ private val hideToolbarFeature = ViewBoundFeatureWrapper()
override fun initializeUI(view: View): Session? {
return super.initializeUI(view)?.also {
@@ -41,18 +52,61 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
requireComponents.core.sessionManager,
toolbar,
customTabSessionId,
- activity,
+ activity!!,
view.nestedScrollQuickAction,
view.swipeRefresh,
onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }
),
owner = this,
view = view)
+
+ val trustedScopes = args.trustedScopes.toList().map { it.toUri() }
+ hideToolbarFeature.set(
+ feature = WebAppHideToolbarFeature(
+ requireComponents.core.sessionManager,
+ toolbar,
+ customTabSessionId,
+ trustedScopes
+ ),
+ owner = this,
+ view = toolbar)
+ // Hot-fix until there's a hideToolbarFeature.onHideStateChanged
+ if (trustedScopes.isNotEmpty()) {
+ getSessionById()?.register(object : Session.Observer {
+ override fun onUrlChanged(session: Session, url: String) {
+ updateLayoutMargins(false)
+ }
+ })
+ }
+
+ val manifest = args.webAppManifest?.let { json ->
+ WebAppManifestParser().parse(json).getOrNull()
+ }
+ if (manifest != null) {
+ activity?.lifecycle?.addObserver(
+ WebAppActivityFeature(
+ activity!!,
+ requireComponents.core.icons,
+ manifest
+ )
+ )
+ activity?.lifecycle?.addObserver(
+ WebAppSiteControlsFeature(
+ context?.applicationContext!!,
+ requireComponents.core.sessionManager,
+ requireComponents.useCases.sessionUseCases.reload,
+ customTabSessionId,
+ manifest
+ )
+ )
+ }
}
consumeFrom(browserStore) {
browserToolbarView.update(it)
}
+
+ updateLayoutMargins(false)
}
}
@@ -79,8 +133,13 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
}
override fun getEngineMargins(): Pair {
- val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
- return toolbarSize to 0
+ val toolbarHidden = toolbar.isGone
+ return if (toolbarHidden) {
+ 0 to 0
+ } else {
+ val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
+ toolbarSize to 0
+ }
}
override fun getAppropriateLayoutGravity() = Gravity.TOP
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index bfd262cdd748..bf4e5a0047a8 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -201,6 +201,8 @@
+
+
Your Library
Desktop site
+
+ Add to homescreen
Find in page
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 1b66d73ca0af..c7952636f901 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -122,6 +122,7 @@ object Deps {
const val mozilla_feature_storage = "org.mozilla.components:feature-storage:${Versions.mozilla_android_components}"
const val mozilla_feature_prompts = "org.mozilla.components:feature-prompts:${Versions.mozilla_android_components}"
const val mozilla_feature_push = "org.mozilla.components:feature-push:${Versions.mozilla_android_components}"
+ const val mozilla_feature_pwa = "org.mozilla.components:feature-pwa:${Versions.mozilla_android_components}"
const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}"
const val mozilla_feature_findinpage = "org.mozilla.components:feature-findinpage:${Versions.mozilla_android_components}"
const val mozilla_feature_site_permissions = "org.mozilla.components:feature-sitepermissions:${Versions.mozilla_android_components}"