| 
Commits:
af11e480
 by t-p-white   at 2023-12-14T20:08:19+01:00 
 Bug 1823316 - Use 'Snackbar' themed Dialog to notify on making app full-screen
068e68e8
 by Tarik Eshaq   at 2023-12-14T20:09:55+01:00 
 Bug 1865488: Adds server parameter to push subscription
(cherry picked from commit f66bc9d4981d9bba7091389d9f0a6864291d38fe)
 
9 changed files:
Changes:
android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
 
|  | 1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
 |  
|  | 2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
 |  
|  | 3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 |  
|  | 4 | +
 |  
|  | 5 | +package mozilla.components.feature.prompts.dialog
 |  
|  | 6 | +
 |  
|  | 7 | +import android.app.Dialog
 |  
|  | 8 | +import android.os.Bundle
 |  
|  | 9 | +import android.view.Gravity
 |  
|  | 10 | +import android.view.WindowManager
 |  
|  | 11 | +import androidx.annotation.LayoutRes
 |  
|  | 12 | +import androidx.appcompat.app.AlertDialog
 |  
|  | 13 | +import androidx.fragment.app.DialogFragment
 |  
|  | 14 | +import androidx.fragment.app.FragmentManager
 |  
|  | 15 | +import androidx.lifecycle.lifecycleScope
 |  
|  | 16 | +import kotlinx.coroutines.delay
 |  
|  | 17 | +import kotlinx.coroutines.launch
 |  
|  | 18 | +
 |  
|  | 19 | +private const val TAG = "mozac_feature_prompts_full_screen_notification_dialog"
 |  
|  | 20 | +private const val SNACKBAR_DURATION_LONG_MS = 3000L
 |  
|  | 21 | +
 |  
|  | 22 | +/**
 |  
|  | 23 | + * UI to show a 'full screen mode' notification.
 |  
|  | 24 | + */
 |  
|  | 25 | +interface FullScreenNotification {
 |  
|  | 26 | +    /**
 |  
|  | 27 | +     * Show the notification.
 |  
|  | 28 | +     *
 |  
|  | 29 | +     * @param fragmentManager the [FragmentManager] to add this notification to.
 |  
|  | 30 | +     */
 |  
|  | 31 | +    fun show(fragmentManager: FragmentManager)
 |  
|  | 32 | +}
 |  
|  | 33 | +
 |  
|  | 34 | +/**
 |  
|  | 35 | + * [DialogFragment] that is configured to match the style and behaviour of a Snackbar.
 |  
|  | 36 | + *
 |  
|  | 37 | + * @property layout the layout to use for the dialog.
 |  
|  | 38 | + */
 |  
|  | 39 | +class FullScreenNotificationDialog(@LayoutRes val layout: Int) :
 |  
|  | 40 | +    DialogFragment(), FullScreenNotification {
 |  
|  | 41 | +    override fun show(fragmentManager: FragmentManager) = super.show(fragmentManager, TAG)
 |  
|  | 42 | +
 |  
|  | 43 | +    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = requireActivity().let {
 |  
|  | 44 | +        val view = layoutInflater.inflate(layout, null)
 |  
|  | 45 | +        AlertDialog.Builder(it).setView(view).create()
 |  
|  | 46 | +    }
 |  
|  | 47 | +
 |  
|  | 48 | +    override fun onStart() {
 |  
|  | 49 | +        super.onStart()
 |  
|  | 50 | +
 |  
|  | 51 | +        dialog?.let { dialog ->
 |  
|  | 52 | +            dialog.window?.let { window ->
 |  
|  | 53 | +                // Prevent any user input from key or other button events to it.
 |  
|  | 54 | +                window.setFlags(
 |  
|  | 55 | +                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
 |  
|  | 56 | +                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
 |  
|  | 57 | +                )
 |  
|  | 58 | +
 |  
|  | 59 | +                window.setGravity(Gravity.BOTTOM)
 |  
|  | 60 | +                window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
 |  
|  | 61 | +            }
 |  
|  | 62 | +
 |  
|  | 63 | +            lifecycleScope.launch {
 |  
|  | 64 | +                delay(SNACKBAR_DURATION_LONG_MS)
 |  
|  | 65 | +                dismiss()
 |  
|  | 66 | +            }
 |  
|  | 67 | +        }
 |  
|  | 68 | +    }
 |  
|  | 69 | +} |  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 |  
| 70 | 70 |  import java.security.InvalidParameterException
 |  
| 71 | 71 |  import mozilla.components.ui.icons.R as iconsR
 |  
| 72 | 72 |  
 |  
| 73 |  | -internal const val FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
 |  
|  | 73 | +internal const val PROMPT_FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
 |  
|  | 74 | +
 |  
|  | 75 | +private const val FULL_SCREEN_NOTIFICATION_TAG = "mozac_feature_prompts_full_screen_notification_dialog"
 |  
| 74 | 76 |  
 |  
| 75 | 77 |  @VisibleForTesting
 |  
| 76 | 78 |  internal const val STORAGE_ACCESS_DOCUMENTATION_URL =
 |  
| ... | ... | @@ -124,7 +126,7 @@ class SitePermissionsFeature( |  
| 124 | 126 |      private var loadingScope: CoroutineScope? = null
 |  
| 125 | 127 |  
 |  
| 126 | 128 |      override fun start() {
 |  
| 127 |  | -        fragmentManager.findFragmentByTag(FRAGMENT_TAG)?.let { fragment ->
 |  
|  | 129 | +        fragmentManager.findFragmentByTag(PROMPT_FRAGMENT_TAG)?.let { fragment ->
 |  
| 128 | 130 |              // There's still a [SitePermissionsDialogFragment] visible from the last time. Re-attach
 |  
| 129 | 131 |              // this feature so that the fragment can invoke the callback on this feature once the user
 |  
| 130 | 132 |              // makes a selection. This can happen when the app was in the background and on resume
 |  
| ... | ... | @@ -439,8 +441,16 @@ class SitePermissionsFeature( |  
| 439 | 441 |          } else {
 |  
| 440 | 442 |              handleNoRuledFlow(permissionFromStorage, permissionRequest, origin)
 |  
| 441 | 443 |          }
 |  
| 442 |  | -        prompt?.show(fragmentManager, FRAGMENT_TAG)
 |  
| 443 |  | -        return prompt
 |  
|  | 444 | +
 |  
|  | 445 | +        val fullScreenNotificationDisplayed =
 |  
|  | 446 | +            fragmentManager.fragments.any { fragment -> fragment.tag == FULL_SCREEN_NOTIFICATION_TAG }
 |  
|  | 447 | +
 |  
|  | 448 | +        return if (fullScreenNotificationDisplayed || prompt == null) {
 |  
|  | 449 | +            null
 |  
|  | 450 | +        } else {
 |  
|  | 451 | +            prompt.show(fragmentManager, PROMPT_FRAGMENT_TAG)
 |  
|  | 452 | +            prompt
 |  
|  | 453 | +        }
 |  
| 444 | 454 |      }
 |  
| 445 | 455 |  
 |  
| 446 | 456 |      @VisibleForTesting
 |  fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
 
 
| ... | ... | @@ -17,7 +17,6 @@ import android.view.LayoutInflater |  
| 17 | 17 |  import android.view.View
 |  
| 18 | 18 |  import android.view.ViewGroup
 |  
| 19 | 19 |  import android.view.accessibility.AccessibilityManager
 |  
| 20 |  | -import android.widget.Toast
 |  
| 21 | 20 |  import androidx.activity.result.ActivityResultLauncher
 |  
| 22 | 21 |  import androidx.annotation.CallSuper
 |  
| 23 | 22 |  import androidx.annotation.VisibleForTesting
 |  
| ... | ... | @@ -74,6 +73,7 @@ import mozilla.components.feature.prompts.PromptFeature |  
| 74 | 73 |  import mozilla.components.feature.prompts.PromptFeature.Companion.PIN_REQUEST
 |  
| 75 | 74 |  import mozilla.components.feature.prompts.address.AddressDelegate
 |  
| 76 | 75 |  import mozilla.components.feature.prompts.creditcard.CreditCardDelegate
 |  
|  | 76 | +import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
 |  
| 77 | 77 |  import mozilla.components.feature.prompts.login.LoginDelegate
 |  
| 78 | 78 |  import mozilla.components.feature.prompts.share.ShareDelegate
 |  
| 79 | 79 |  import mozilla.components.feature.readerview.ReaderViewFeature
 |  
| ... | ... | @@ -1465,10 +1465,11 @@ abstract class BaseBrowserFragment : |  
| 1465 | 1465 |          if (inFullScreen) {
 |  
| 1466 | 1466 |              // Close find in page bar if opened
 |  
| 1467 | 1467 |              findInPageIntegration.onBackPressed()
 |  
| 1468 |  | -            Toast
 |  
| 1469 |  | -                .makeText(requireContext(), R.string.full_screen_notification, Toast.LENGTH_SHORT)
 |  
| 1470 |  | -                .show()
 |  
| 1471 |  | -            activity?.enterToImmersiveMode()
 |  
|  | 1468 | +
 |  
|  | 1469 | +            FullScreenNotificationDialog(R.layout.full_screen_notification_dialog).show(
 |  
|  | 1470 | +                parentFragmentManager,
 |  
|  | 1471 | +            )
 |  
|  | 1472 | +
 |  
| 1472 | 1473 |              (view as? SwipeGestureLayout)?.isSwipeEnabled = false
 |  
| 1473 | 1474 |              browserToolbarView.collapse()
 |  
| 1474 | 1475 |              browserToolbarView.view.isVisible = false
 |  fenix/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt
 
 
| ... | ... | @@ -58,8 +58,6 @@ internal class WebPushEngineDelegate( |  
| 58 | 58 |      private val logger = Logger("WebPushEngineDelegate")
 |  
| 59 | 59 |  
 |  
| 60 | 60 |      override fun onGetSubscription(scope: String, onSubscription: (WebPushSubscription?) -> Unit) {
 |  
| 61 |  | -        // We don't have the appServerKey unless an app is creating a new subscription so we
 |  
| 62 |  | -        // allow the key to be null since it won't be overridden from a previous subscription.
 |  
| 63 | 61 |          pushFeature.getSubscription(scope) {
 |  
| 64 | 62 |              onSubscription(it?.toEnginePushSubscription())
 |  
| 65 | 63 |          }
 |  
| ... | ... | @@ -72,9 +70,7 @@ internal class WebPushEngineDelegate( |  
| 72 | 70 |      ) {
 |  
| 73 | 71 |          pushFeature.subscribe(
 |  
| 74 | 72 |              scope = scope,
 |  
| 75 |  | -            // See the full note at the implementation of `toEnginePushSubscription`.
 |  
| 76 |  | -            // Issue: https://github.com/mozilla/application-services/issues/2698
 |  
| 77 |  | -            appServerKey = null,
 |  
|  | 73 | +            appServerKey = serverKey?.toEncodedBase64String(),
 |  
| 78 | 74 |              onSubscribeError = {
 |  
| 79 | 75 |                  logger.error("Error on push onSubscribe.")
 |  
| 80 | 76 |                  onSubscribe(null)
 |  
| ... | ... | @@ -104,13 +100,12 @@ internal fun AutoPushSubscription.toEnginePushSubscription() = WebPushSubscripti |  
| 104 | 100 |      publicKey = this.publicKey.toDecodedByteArray(),
 |  
| 105 | 101 |      endpoint = this.endpoint,
 |  
| 106 | 102 |      authSecret = this.authKey.toDecodedByteArray(),
 |  
| 107 |  | -    // We don't send the `serverKey` because the code path from that will query
 |  
| 108 |  | -    // the push database for this key, which leads to an exception thrown.
 |  
| 109 |  | -    // Our workaround for now is to not put the server key in to begin with (which
 |  
| 110 |  | -    // will probably break a lot of sites).
 |  
| 111 |  | -    // See: https://github.com/mozilla/application-services/issues/2698
 |  
|  | 103 | +    // We don't have the appServerKey unless an app is creating a new subscription so we
 |  
|  | 104 | +    // allow the key to be null since it won't be overridden from a previous subscription.
 |  
| 112 | 105 |      appServerKey = null,
 |  
| 113 | 106 |  )
 |  
| 114 | 107 |  
 |  
| 115 | 108 |  private fun String.toDecodedByteArray() =
 |  
| 116 | 109 |      Base64.decode(this.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)
 |  
|  | 110 | +private fun ByteArray.toEncodedBase64String() =
 |  
|  | 111 | +    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
 
 
|  | 1 | +<?xml version="1.0" encoding="utf-8"?>
 |  
|  | 2 | +<!-- This Source Code Form is subject to the terms of the Mozilla Public
 |  
|  | 3 | +   - License, v. 2.0. If a copy of the MPL was not distributed with this
 |  
|  | 4 | +   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 |  
|  | 5 | +
 |  
|  | 6 | +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 |  
|  | 7 | +    xmlns:app="http://schemas.android.com/apk/res-auto"
 |  
|  | 8 | +    xmlns:tools="http://schemas.android.com/tools"
 |  
|  | 9 | +    android:id="@+id/full_screen_notification"
 |  
|  | 10 | +    android:layout_width="match_parent"
 |  
|  | 11 | +    android:layout_height="wrap_content"
 |  
|  | 12 | +    android:layout_margin="8dp"
 |  
|  | 13 | +    android:background="">"@drawable/fenix_snackbar_background"
 |  
|  | 14 | +    android:elevation="4dp"
 |  
|  | 15 | +    android:minHeight="48dp"
 |  
|  | 16 | +    android:orientation="horizontal"
 |  
|  | 17 | +    android:paddingStart="16dp"
 |  
|  | 18 | +    android:paddingEnd="16dp">
 |  
|  | 19 | +
 |  
|  | 20 | +    <TextView
 |  
|  | 21 | +        android:id="@+id/full_screen_notification_text"
 |  
|  | 22 | +        android:layout_width="0dp"
 |  
|  | 23 | +        android:layout_height="wrap_content"
 |  
|  | 24 | +        android:ellipsize="end"
 |  
|  | 25 | +        android:letterSpacing="0.05"
 |  
|  | 26 | +        android:maxLines="2"
 |  
|  | 27 | +        android:minHeight="46dp"
 |  
|  | 28 | +        android:paddingTop="8dp"
 |  
|  | 29 | +        android:paddingBottom="8dp"
 |  
|  | 30 | +        android:text="@string/full_screen_notification"
 |  
|  | 31 | +        android:textAlignment="textStart"
 |  
|  | 32 | +        android:textColor="@color/photonWhite"
 |  
|  | 33 | +        android:textSize="18sp"
 |  
|  | 34 | +        app:layout_constraintBottom_toBottomOf="parent"
 |  
|  | 35 | +        app:layout_constraintEnd_toEndOf="parent"
 |  
|  | 36 | +        app:layout_constraintStart_toStartOf="parent"
 |  
|  | 37 | +        app:layout_constraintTop_toTopOf="parent"
 |  
|  | 38 | +        tools:text="@string/full_screen_notification" />
 |  
|  | 39 | +
 |  
|  | 40 | +</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 |  
| 7 | 7 |  import android.app.Activity
 |  
| 8 | 8 |  import android.os.Build
 |  
| 9 | 9 |  import android.view.View
 |  
| 10 |  | -import android.widget.Toast
 |  
| 11 | 10 |  import androidx.annotation.VisibleForTesting
 |  
| 12 | 11 |  import androidx.core.view.isVisible
 |  
|  | 12 | +import androidx.fragment.app.FragmentManager
 |  
| 13 | 13 |  import mozilla.components.browser.state.store.BrowserStore
 |  
| 14 | 14 |  import mozilla.components.browser.toolbar.BrowserToolbar
 |  
| 15 | 15 |  import mozilla.components.concept.engine.EngineView
 |  
|  | 16 | +import mozilla.components.feature.prompts.dialog.FullScreenNotification
 |  
|  | 17 | +import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
 |  
| 16 | 18 |  import mozilla.components.feature.session.FullScreenFeature
 |  
| 17 | 19 |  import mozilla.components.feature.session.SessionUseCases
 |  
| 18 | 20 |  import mozilla.components.support.base.feature.LifecycleAwareFeature
 |  
| ... | ... | @@ -26,6 +28,7 @@ import org.mozilla.focus.ext.hide |  
| 26 | 28 |  import org.mozilla.focus.ext.showAsFixed
 |  
| 27 | 29 |  import org.mozilla.focus.utils.Settings
 |  
| 28 | 30 |  
 |  
|  | 31 | +@Suppress("LongParameterList")
 |  
| 29 | 32 |  class FullScreenIntegration(
 |  
| 30 | 33 |      val activity: Activity,
 |  
| 31 | 34 |      val store: BrowserStore,
 |  
| ... | ... | @@ -35,6 +38,7 @@ class FullScreenIntegration( |  
| 35 | 38 |      private val toolbarView: BrowserToolbar,
 |  
| 36 | 39 |      private val statusBar: View,
 |  
| 37 | 40 |      private val engineView: EngineView,
 |  
|  | 41 | +    private val parentFragmentManager: FragmentManager,
 |  
| 38 | 42 |  ) : LifecycleAwareFeature, UserInteractionHandler {
 |  
| 39 | 43 |      @VisibleForTesting
 |  
| 40 | 44 |      internal var feature = FullScreenFeature(
 |  
| ... | ... | @@ -54,14 +58,16 @@ class FullScreenIntegration( |  
| 54 | 58 |      }
 |  
| 55 | 59 |  
 |  
| 56 | 60 |      @VisibleForTesting
 |  
| 57 |  | -    internal fun fullScreenChanged(enabled: Boolean) {
 |  
|  | 61 | +    internal fun fullScreenChanged(
 |  
|  | 62 | +        enabled: Boolean,
 |  
|  | 63 | +        fullScreenNotification: FullScreenNotification =
 |  
|  | 64 | +            FullScreenNotificationDialog(R.layout.dialog_full_screen_notification),
 |  
|  | 65 | +    ) {
 |  
| 58 | 66 |          if (enabled) {
 |  
| 59 | 67 |              enterBrowserFullscreen()
 |  
| 60 | 68 |              statusBar.isVisible = false
 |  
| 61 | 69 |  
 |  
| 62 |  | -            Toast
 |  
| 63 |  | -                .makeText(activity, R.string.full_screen_notification, Toast.LENGTH_SHORT)
 |  
| 64 |  | -                .show()
 |  
|  | 70 | +            fullScreenNotification.show(parentFragmentManager)
 |  
| 65 | 71 |  
 |  
| 66 | 72 |              switchToImmersiveMode()
 |  
| 67 | 73 |          } else {
 |  focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
 
 
| ... | ... | @@ -272,6 +272,7 @@ class BrowserFragment : |  
| 272 | 272 |                  binding.browserToolbar,
 |  
| 273 | 273 |                  binding.statusBarBackground,
 |  
| 274 | 274 |                  binding.engineView,
 |  
|  | 275 | +                parentFragmentManager,
 |  
| 275 | 276 |              ),
 |  
| 276 | 277 |              this,
 |  
| 277 | 278 |              view,
 |  focus-android/app/src/main/res/layout/dialog_full_screen_notification.xml
 
 
|  | 1 | +<?xml version="1.0" encoding="utf-8"?>
 |  
|  | 2 | +<!-- This Source Code Form is subject to the terms of the Mozilla Public
 |  
|  | 3 | +   - License, v. 2.0. If a copy of the MPL was not distributed with this
 |  
|  | 4 | +   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 |  
|  | 5 | +
 |  
|  | 6 | +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 |  
|  | 7 | +    xmlns:app="http://schemas.android.com/apk/res-auto"
 |  
|  | 8 | +    xmlns:tools="http://schemas.android.com/tools"
 |  
|  | 9 | +    android:id="@+id/full_screen_notification_layout"
 |  
|  | 10 | +    android:layout_width="match_parent"
 |  
|  | 11 | +    android:layout_height="match_parent"
 |  
|  | 12 | +    android:layout_margin="8dp"
 |  
|  | 13 | +    android:background="">"@drawable/focus_snackbar_background"
 |  
|  | 14 | +    android:elevation="4dp"
 |  
|  | 15 | +    android:minHeight="48dp"
 |  
|  | 16 | +    android:orientation="horizontal"
 |  
|  | 17 | +    android:paddingStart="16dp"
 |  
|  | 18 | +    android:paddingEnd="16dp">
 |  
|  | 19 | +
 |  
|  | 20 | +    <TextView
 |  
|  | 21 | +        android:id="@+id/full_screen_notification_text"
 |  
|  | 22 | +        android:layout_width="0dp"
 |  
|  | 23 | +        android:layout_height="wrap_content"
 |  
|  | 24 | +        android:ellipsize="end"
 |  
|  | 25 | +        android:letterSpacing="0.05"
 |  
|  | 26 | +        android:maxLines="2"
 |  
|  | 27 | +        android:paddingTop="8dp"
 |  
|  | 28 | +        android:paddingBottom="8dp"
 |  
|  | 29 | +        android:text="@string/full_screen_notification"
 |  
|  | 30 | +        android:textAlignment="textStart"
 |  
|  | 31 | +        android:textColor="@color/snackbarTextColor"
 |  
|  | 32 | +        android:textSize="14sp"
 |  
|  | 33 | +        app:layout_constraintBottom_toBottomOf="parent"
 |  
|  | 34 | +        app:layout_constraintEnd_toEndOf="parent"
 |  
|  | 35 | +        app:layout_constraintStart_toStartOf="parent"
 |  
|  | 36 | +        app:layout_constraintTop_toTopOf="parent"
 |  
|  | 37 | +        tools:text="@string/full_screen_notification" />
 |  
|  | 38 | +</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 |  
| 12 | 12 |  import androidx.core.view.isVisible
 |  
| 13 | 13 |  import mozilla.components.browser.engine.gecko.GeckoEngineView
 |  
| 14 | 14 |  import mozilla.components.browser.toolbar.BrowserToolbar
 |  
|  | 15 | +import mozilla.components.feature.prompts.dialog.FullScreenNotification
 |  
| 15 | 16 |  import mozilla.components.feature.session.FullScreenFeature
 |  
| 16 | 17 |  import mozilla.components.support.test.any
 |  
| 17 | 18 |  import mozilla.components.support.test.mock
 |  
| 18 | 19 |  import mozilla.components.support.test.robolectric.testContext
 |  
| 19 |  | -import org.junit.Assert.assertNotNull
 |  
| 20 | 20 |  import org.junit.Test
 |  
| 21 | 21 |  import org.junit.jupiter.api.Assertions.assertEquals
 |  
| 22 | 22 |  import org.junit.runner.RunWith
 |  
| ... | ... | @@ -26,7 +26,6 @@ import org.mockito.Mockito.never |  
| 26 | 26 |  import org.mockito.Mockito.spy
 |  
| 27 | 27 |  import org.mockito.Mockito.times
 |  
| 28 | 28 |  import org.mockito.Mockito.verify
 |  
| 29 |  | -import org.mozilla.focus.R
 |  
| 30 | 29 |  import org.mozilla.focus.ext.disableDynamicBehavior
 |  
| 31 | 30 |  import org.mozilla.focus.ext.enableDynamicBehavior
 |  
| 32 | 31 |  import org.mozilla.focus.ext.hide
 |  
| ... | ... | @@ -34,7 +33,6 @@ import org.mozilla.focus.ext.showAsFixed |  
| 34 | 33 |  import org.mozilla.focus.utils.Settings
 |  
| 35 | 34 |  import org.robolectric.Robolectric
 |  
| 36 | 35 |  import org.robolectric.RobolectricTestRunner
 |  
| 37 |  | -import org.robolectric.shadows.ShadowToast
 |  
| 38 | 36 |  
 |  
| 39 | 37 |  @RunWith(RobolectricTestRunner::class)
 |  
| 40 | 38 |  internal class FullScreenIntegrationTest {
 |  
| ... | ... | @@ -50,6 +48,7 @@ internal class FullScreenIntegrationTest { |  
| 50 | 48 |              mock(),
 |  
| 51 | 49 |              mock(),
 |  
| 52 | 50 |              mock(),
 |  
|  | 51 | +            mock(),
 |  
| 53 | 52 |          ).apply {
 |  
| 54 | 53 |              this.feature = feature
 |  
| 55 | 54 |          }
 |  
| ... | ... | @@ -71,6 +70,7 @@ internal class FullScreenIntegrationTest { |  
| 71 | 70 |              mock(),
 |  
| 72 | 71 |              mock(),
 |  
| 73 | 72 |              mock(),
 |  
|  | 73 | +            mock(),
 |  
| 74 | 74 |          ).apply {
 |  
| 75 | 75 |              this.feature = feature
 |  
| 76 | 76 |          }
 |  
| ... | ... | @@ -92,6 +92,7 @@ internal class FullScreenIntegrationTest { |  
| 92 | 92 |              mock(),
 |  
| 93 | 93 |              mock(),
 |  
| 94 | 94 |              mock(),
 |  
|  | 95 | +            mock(),
 |  
| 95 | 96 |          ).apply {
 |  
| 96 | 97 |              this.feature = feature
 |  
| 97 | 98 |          }
 |  
| ... | ... | @@ -117,6 +118,7 @@ internal class FullScreenIntegrationTest { |  
| 117 | 118 |              mock(),
 |  
| 118 | 119 |              mock(),
 |  
| 119 | 120 |              mock(),
 |  
|  | 121 | +            mock(),
 |  
| 120 | 122 |          )
 |  
| 121 | 123 |  
 |  
| 122 | 124 |          integration.viewportFitChanged(33)
 |  
| ... | ... | @@ -141,6 +143,7 @@ internal class FullScreenIntegrationTest { |  
| 141 | 143 |              mock(),
 |  
| 142 | 144 |              mock(),
 |  
| 143 | 145 |              mock(),
 |  
|  | 146 | +            mock(),
 |  
| 144 | 147 |          )
 |  
| 145 | 148 |  
 |  
| 146 | 149 |          integration.switchToImmersiveMode()
 |  
| ... | ... | @@ -169,6 +172,7 @@ internal class FullScreenIntegrationTest { |  
| 169 | 172 |              mock(),
 |  
| 170 | 173 |              mock(),
 |  
| 171 | 174 |              mock(),
 |  
|  | 175 | +            mock(),
 |  
| 172 | 176 |          )
 |  
| 173 | 177 |  
 |  
| 174 | 178 |          integration.exitImmersiveMode()
 |  
| ... | ... | @@ -195,6 +199,7 @@ internal class FullScreenIntegrationTest { |  
| 195 | 199 |              toolbar,
 |  
| 196 | 200 |              mock(),
 |  
| 197 | 201 |              engineView,
 |  
|  | 202 | +            mock(),
 |  
| 198 | 203 |          )
 |  
| 199 | 204 |  
 |  
| 200 | 205 |          integration.enterBrowserFullscreen()
 |  
| ... | ... | @@ -220,6 +225,7 @@ internal class FullScreenIntegrationTest { |  
| 220 | 225 |              toolbar,
 |  
| 221 | 226 |              mock(),
 |  
| 222 | 227 |              engineView,
 |  
|  | 228 | +            mock(),
 |  
| 223 | 229 |          )
 |  
| 224 | 230 |  
 |  
| 225 | 231 |          integration.enterBrowserFullscreen()
 |  
| ... | ... | @@ -250,6 +256,7 @@ internal class FullScreenIntegrationTest { |  
| 250 | 256 |              toolbar,
 |  
| 251 | 257 |              mock(),
 |  
| 252 | 258 |              engineView,
 |  
|  | 259 | +            mock(),
 |  
| 253 | 260 |          )
 |  
| 254 | 261 |  
 |  
| 255 | 262 |          integration.exitBrowserFullscreen()
 |  
| ... | ... | @@ -278,6 +285,7 @@ internal class FullScreenIntegrationTest { |  
| 278 | 285 |              toolbar,
 |  
| 279 | 286 |              mock(),
 |  
| 280 | 287 |              engineView,
 |  
|  | 288 | +            mock(),
 |  
| 281 | 289 |          )
 |  
| 282 | 290 |  
 |  
| 283 | 291 |          integration.exitBrowserFullscreen()
 |  
| ... | ... | @@ -308,21 +316,17 @@ internal class FullScreenIntegrationTest { |  
| 308 | 316 |                  toolbar,
 |  
| 309 | 317 |                  statusBar,
 |  
| 310 | 318 |                  engineView,
 |  
|  | 319 | +                mock(),
 |  
| 311 | 320 |              ),
 |  
| 312 | 321 |          )
 |  
| 313 | 322 |  
 |  
| 314 |  | -        integration.fullScreenChanged(true)
 |  
|  | 323 | +        val fullScreenNotification = mock<FullScreenNotification>()
 |  
|  | 324 | +        integration.fullScreenChanged(true, fullScreenNotification)
 |  
| 315 | 325 |  
 |  
| 316 | 326 |          verify(integration).enterBrowserFullscreen()
 |  
| 317 |  | -        verify(integration).switchToImmersiveMode()
 |  
| 318 | 327 |          verify(statusBar).isVisible = false
 |  
| 319 |  | -
 |  
| 320 |  | -        val toast = ShadowToast.getTextOfLatestToast()
 |  
| 321 |  | -        assertNotNull(toast)
 |  
| 322 |  | -        assertEquals(
 |  
| 323 |  | -            testContext.getString(R.string.full_screen_notification),
 |  
| 324 |  | -            toast,
 |  
| 325 |  | -        )
 |  
|  | 328 | +        verify(fullScreenNotification).show(any())
 |  
|  | 329 | +        verify(integration).switchToImmersiveMode()
 |  
| 326 | 330 |      }
 |  
| 327 | 331 |  
 |  
| 328 | 332 |      @Test
 |  
| ... | ... | @@ -352,6 +356,7 @@ internal class FullScreenIntegrationTest { |  
| 352 | 356 |                  toolbar,
 |  
| 353 | 357 |                  statusBar,
 |  
| 354 | 358 |                  engineView,
 |  
|  | 359 | +                mock(),
 |  
| 355 | 360 |              ),
 |  
| 356 | 361 |          )
 |  
| 357 | 362 |  
 |  
 |