[tor-commits] [Git][tpo/applications/firefox-android][firefox-android-115.2.1-13.0-1] 2 commits: Bug 1823316 - Use 'Snackbar' themed Dialog to notify on making app full-screen
richard (@richard)
git at gitlab.torproject.org
Thu Dec 14 18:54:50 UTC 2023
richard pushed to branch firefox-android-115.2.1-13.0-1 at The Tor Project / Applications / firefox-android
Commits:
a0805d02 by t-p-white at 2023-12-14T15:36:13+01:00
Bug 1823316 - Use 'Snackbar' themed Dialog to notify on making app full-screen
- - - - -
8ff2dd1b by Tarik Eshaq at 2023-12-14T15:36:22+01:00
Bug 1865488: Adds server parameter to push subscription
(cherry picked from commit f66bc9d4981d9bba7091389d9f0a6864291d38fe)
- - - - -
9 changed files:
- + android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
- android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsFeature.kt
- fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
- fenix/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt
- + fenix/app/src/main/res/layout/full_screen_notification_dialog.xml
- focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
- focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
- + focus-android/app/src/main/res/layout/dialog_full_screen_notification.xml
- focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
Changes:
=====================================
android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
=====================================
@@ -0,0 +1,69 @@
+/* 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.prompts.dialog
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import androidx.annotation.LayoutRes
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+private const val TAG = "mozac_feature_prompts_full_screen_notification_dialog"
+private const val SNACKBAR_DURATION_LONG_MS = 3000L
+
+/**
+ * UI to show a 'full screen mode' notification.
+ */
+interface FullScreenNotification {
+ /**
+ * Show the notification.
+ *
+ * @param fragmentManager the [FragmentManager] to add this notification to.
+ */
+ fun show(fragmentManager: FragmentManager)
+}
+
+/**
+ * [DialogFragment] that is configured to match the style and behaviour of a Snackbar.
+ *
+ * @property layout the layout to use for the dialog.
+ */
+class FullScreenNotificationDialog(@LayoutRes val layout: Int) :
+ DialogFragment(), FullScreenNotification {
+ override fun show(fragmentManager: FragmentManager) = super.show(fragmentManager, TAG)
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = requireActivity().let {
+ val view = layoutInflater.inflate(layout, null)
+ AlertDialog.Builder(it).setView(view).create()
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ dialog?.let { dialog ->
+ dialog.window?.let { window ->
+ // Prevent any user input from key or other button events to it.
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ )
+
+ window.setGravity(Gravity.BOTTOM)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ }
+
+ lifecycleScope.launch {
+ delay(SNACKBAR_DURATION_LONG_MS)
+ dismiss()
+ }
+ }
+ }
+}
=====================================
android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsFeature.kt
=====================================
@@ -70,7 +70,9 @@ import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import java.security.InvalidParameterException
import mozilla.components.ui.icons.R as iconsR
-internal const val FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
+internal const val PROMPT_FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
+
+private const val FULL_SCREEN_NOTIFICATION_TAG = "mozac_feature_prompts_full_screen_notification_dialog"
@VisibleForTesting
internal const val STORAGE_ACCESS_DOCUMENTATION_URL =
@@ -124,7 +126,7 @@ class SitePermissionsFeature(
private var loadingScope: CoroutineScope? = null
override fun start() {
- fragmentManager.findFragmentByTag(FRAGMENT_TAG)?.let { fragment ->
+ fragmentManager.findFragmentByTag(PROMPT_FRAGMENT_TAG)?.let { fragment ->
// There's still a [SitePermissionsDialogFragment] visible from the last time. Re-attach
// this feature so that the fragment can invoke the callback on this feature once the user
// makes a selection. This can happen when the app was in the background and on resume
@@ -439,8 +441,16 @@ class SitePermissionsFeature(
} else {
handleNoRuledFlow(permissionFromStorage, permissionRequest, origin)
}
- prompt?.show(fragmentManager, FRAGMENT_TAG)
- return prompt
+
+ val fullScreenNotificationDisplayed =
+ fragmentManager.fragments.any { fragment -> fragment.tag == FULL_SCREEN_NOTIFICATION_TAG }
+
+ return if (fullScreenNotificationDisplayed || prompt == null) {
+ null
+ } else {
+ prompt.show(fragmentManager, PROMPT_FRAGMENT_TAG)
+ prompt
+ }
}
@VisibleForTesting
=====================================
fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
=====================================
@@ -17,7 +17,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityManager
-import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
@@ -74,6 +73,7 @@ import mozilla.components.feature.prompts.PromptFeature
import mozilla.components.feature.prompts.PromptFeature.Companion.PIN_REQUEST
import mozilla.components.feature.prompts.address.AddressDelegate
import mozilla.components.feature.prompts.creditcard.CreditCardDelegate
+import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
import mozilla.components.feature.prompts.login.LoginDelegate
import mozilla.components.feature.prompts.share.ShareDelegate
import mozilla.components.feature.readerview.ReaderViewFeature
@@ -1465,10 +1465,11 @@ abstract class BaseBrowserFragment :
if (inFullScreen) {
// Close find in page bar if opened
findInPageIntegration.onBackPressed()
- Toast
- .makeText(requireContext(), R.string.full_screen_notification, Toast.LENGTH_SHORT)
- .show()
- activity?.enterToImmersiveMode()
+
+ FullScreenNotificationDialog(R.layout.full_screen_notification_dialog).show(
+ parentFragmentManager,
+ )
+
(view as? SwipeGestureLayout)?.isSwipeEnabled = false
browserToolbarView.collapse()
browserToolbarView.view.isVisible = false
=====================================
fenix/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt
=====================================
@@ -58,8 +58,6 @@ internal class WebPushEngineDelegate(
private val logger = Logger("WebPushEngineDelegate")
override fun onGetSubscription(scope: String, onSubscription: (WebPushSubscription?) -> Unit) {
- // We don't have the appServerKey unless an app is creating a new subscription so we
- // allow the key to be null since it won't be overridden from a previous subscription.
pushFeature.getSubscription(scope) {
onSubscription(it?.toEnginePushSubscription())
}
@@ -72,9 +70,7 @@ internal class WebPushEngineDelegate(
) {
pushFeature.subscribe(
scope = scope,
- // See the full note at the implementation of `toEnginePushSubscription`.
- // Issue: https://github.com/mozilla/application-services/issues/2698
- appServerKey = null,
+ appServerKey = serverKey?.toEncodedBase64String(),
onSubscribeError = {
logger.error("Error on push onSubscribe.")
onSubscribe(null)
@@ -104,13 +100,12 @@ internal fun AutoPushSubscription.toEnginePushSubscription() = WebPushSubscripti
publicKey = this.publicKey.toDecodedByteArray(),
endpoint = this.endpoint,
authSecret = this.authKey.toDecodedByteArray(),
- // We don't send the `serverKey` because the code path from that will query
- // the push database for this key, which leads to an exception thrown.
- // Our workaround for now is to not put the server key in to begin with (which
- // will probably break a lot of sites).
- // See: https://github.com/mozilla/application-services/issues/2698
+ // We don't have the appServerKey unless an app is creating a new subscription so we
+ // allow the key to be null since it won't be overridden from a previous subscription.
appServerKey = null,
)
private fun String.toDecodedByteArray() =
Base64.decode(this.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
+private fun ByteArray.toEncodedBase64String() =
+ Base64.encodeToString(this, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
=====================================
fenix/app/src/main/res/layout/full_screen_notification_dialog.xml
=====================================
@@ -0,0 +1,40 @@
+<?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/full_screen_notification"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:background="@drawable/fenix_snackbar_background"
+ android:elevation="4dp"
+ android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
+
+ <TextView
+ android:id="@+id/full_screen_notification_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:letterSpacing="0.05"
+ android:maxLines="2"
+ android:minHeight="46dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/full_screen_notification"
+ android:textAlignment="textStart"
+ android:textColor="@color/photonWhite"
+ android:textSize="18sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="@string/full_screen_notification" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
=====================================
focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
=====================================
@@ -7,12 +7,14 @@ package org.mozilla.focus.browser.integration
import android.app.Activity
import android.os.Build
import android.view.View
-import android.widget.Toast
import androidx.annotation.VisibleForTesting
import androidx.core.view.isVisible
+import androidx.fragment.app.FragmentManager
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.EngineView
+import mozilla.components.feature.prompts.dialog.FullScreenNotification
+import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
import mozilla.components.feature.session.FullScreenFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.base.feature.LifecycleAwareFeature
@@ -26,6 +28,7 @@ import org.mozilla.focus.ext.hide
import org.mozilla.focus.ext.showAsFixed
import org.mozilla.focus.utils.Settings
+ at Suppress("LongParameterList")
class FullScreenIntegration(
val activity: Activity,
val store: BrowserStore,
@@ -35,6 +38,7 @@ class FullScreenIntegration(
private val toolbarView: BrowserToolbar,
private val statusBar: View,
private val engineView: EngineView,
+ private val parentFragmentManager: FragmentManager,
) : LifecycleAwareFeature, UserInteractionHandler {
@VisibleForTesting
internal var feature = FullScreenFeature(
@@ -54,14 +58,16 @@ class FullScreenIntegration(
}
@VisibleForTesting
- internal fun fullScreenChanged(enabled: Boolean) {
+ internal fun fullScreenChanged(
+ enabled: Boolean,
+ fullScreenNotification: FullScreenNotification =
+ FullScreenNotificationDialog(R.layout.dialog_full_screen_notification),
+ ) {
if (enabled) {
enterBrowserFullscreen()
statusBar.isVisible = false
- Toast
- .makeText(activity, R.string.full_screen_notification, Toast.LENGTH_SHORT)
- .show()
+ fullScreenNotification.show(parentFragmentManager)
switchToImmersiveMode()
} else {
=====================================
focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
=====================================
@@ -272,6 +272,7 @@ class BrowserFragment :
binding.browserToolbar,
binding.statusBarBackground,
binding.engineView,
+ parentFragmentManager,
),
this,
view,
=====================================
focus-android/app/src/main/res/layout/dialog_full_screen_notification.xml
=====================================
@@ -0,0 +1,38 @@
+<?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/full_screen_notification_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="8dp"
+ android:background="@drawable/focus_snackbar_background"
+ android:elevation="4dp"
+ android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
+
+ <TextView
+ android:id="@+id/full_screen_notification_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:letterSpacing="0.05"
+ android:maxLines="2"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/full_screen_notification"
+ android:textAlignment="textStart"
+ android:textColor="@color/snackbarTextColor"
+ android:textSize="14sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="@string/full_screen_notification" />
+</androidx.constraintlayout.widget.ConstraintLayout>
=====================================
focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
=====================================
@@ -12,11 +12,11 @@ import android.view.WindowManager
import androidx.core.view.isVisible
import mozilla.components.browser.engine.gecko.GeckoEngineView
import mozilla.components.browser.toolbar.BrowserToolbar
+import mozilla.components.feature.prompts.dialog.FullScreenNotification
import mozilla.components.feature.session.FullScreenFeature
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import mozilla.components.support.test.robolectric.testContext
-import org.junit.Assert.assertNotNull
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.runner.RunWith
@@ -26,7 +26,6 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mozilla.focus.R
import org.mozilla.focus.ext.disableDynamicBehavior
import org.mozilla.focus.ext.enableDynamicBehavior
import org.mozilla.focus.ext.hide
@@ -34,7 +33,6 @@ import org.mozilla.focus.ext.showAsFixed
import org.mozilla.focus.utils.Settings
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
-import org.robolectric.shadows.ShadowToast
@RunWith(RobolectricTestRunner::class)
internal class FullScreenIntegrationTest {
@@ -50,6 +48,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
).apply {
this.feature = feature
}
@@ -71,6 +70,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
).apply {
this.feature = feature
}
@@ -92,6 +92,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
).apply {
this.feature = feature
}
@@ -117,6 +118,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
)
integration.viewportFitChanged(33)
@@ -141,6 +143,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
)
integration.switchToImmersiveMode()
@@ -169,6 +172,7 @@ internal class FullScreenIntegrationTest {
mock(),
mock(),
mock(),
+ mock(),
)
integration.exitImmersiveMode()
@@ -195,6 +199,7 @@ internal class FullScreenIntegrationTest {
toolbar,
mock(),
engineView,
+ mock(),
)
integration.enterBrowserFullscreen()
@@ -220,6 +225,7 @@ internal class FullScreenIntegrationTest {
toolbar,
mock(),
engineView,
+ mock(),
)
integration.enterBrowserFullscreen()
@@ -250,6 +256,7 @@ internal class FullScreenIntegrationTest {
toolbar,
mock(),
engineView,
+ mock(),
)
integration.exitBrowserFullscreen()
@@ -278,6 +285,7 @@ internal class FullScreenIntegrationTest {
toolbar,
mock(),
engineView,
+ mock(),
)
integration.exitBrowserFullscreen()
@@ -308,21 +316,17 @@ internal class FullScreenIntegrationTest {
toolbar,
statusBar,
engineView,
+ mock(),
),
)
- integration.fullScreenChanged(true)
+ val fullScreenNotification = mock<FullScreenNotification>()
+ integration.fullScreenChanged(true, fullScreenNotification)
verify(integration).enterBrowserFullscreen()
- verify(integration).switchToImmersiveMode()
verify(statusBar).isVisible = false
-
- val toast = ShadowToast.getTextOfLatestToast()
- assertNotNull(toast)
- assertEquals(
- testContext.getString(R.string.full_screen_notification),
- toast,
- )
+ verify(fullScreenNotification).show(any())
+ verify(integration).switchToImmersiveMode()
}
@Test
@@ -352,6 +356,7 @@ internal class FullScreenIntegrationTest {
toolbar,
statusBar,
engineView,
+ mock(),
),
)
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/compare/332071bd74f249a5da4969091ddc2b268f1715ec...8ff2dd1b1d2063105d5fe7129d32064bda87f422
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/compare/332071bd74f249a5da4969091ddc2b268f1715ec...8ff2dd1b1d2063105d5fe7129d32064bda87f422
You're receiving this email because of your account on gitlab.torproject.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tor-commits/attachments/20231214/5d07a050/attachment-0001.htm>
More information about the tor-commits
mailing list