Richard Pospesel pushed to branch android-components-102.0.14-12.0-1 at The Tor Project / Applications / android-components

Commits:

8 changed files:

Changes:

  • components/browser/toolbar/src/main/java/mozilla/components/browser/toolbar/behavior/BrowserToolbarBehavior.kt
    ... ... @@ -6,19 +6,15 @@ package mozilla.components.browser.toolbar.behavior
    6 6
     
    
    7 7
     import android.content.Context
    
    8 8
     import android.util.AttributeSet
    
    9
    -import android.view.Gravity
    
    10 9
     import android.view.MotionEvent
    
    11 10
     import android.view.View
    
    12 11
     import androidx.annotation.VisibleForTesting
    
    13 12
     import androidx.coordinatorlayout.widget.CoordinatorLayout
    
    14 13
     import androidx.core.view.ViewCompat
    
    15
    -import com.google.android.material.snackbar.Snackbar
    
    16 14
     import mozilla.components.browser.toolbar.BrowserToolbar
    
    17 15
     import mozilla.components.concept.engine.EngineView
    
    18 16
     import mozilla.components.support.ktx.android.view.findViewInHierarchy
    
    19 17
     
    
    20
    -private const val SMALL_ELEVATION_CHANGE = 0.01f
    
    21
    -
    
    22 18
     /**
    
    23 19
      * Where the toolbar is placed on the screen.
    
    24 20
      */
    
    ... ... @@ -35,7 +31,6 @@ enum class ToolbarPosition {
    35 31
      *
    
    36 32
      * This implementation will:
    
    37 33
      * - Show/Hide the [BrowserToolbar] automatically when scrolling vertically.
    
    38
    - * - On showing a [Snackbar] position it above the [BrowserToolbar].
    
    39 34
      * - Snap the [BrowserToolbar] to be hidden or visible when the user stops scrolling.
    
    40 35
      */
    
    41 36
     class BrowserToolbarBehavior(
    
    ... ... @@ -128,14 +123,6 @@ class BrowserToolbarBehavior(
    128 123
             return false // allow events to be passed to below listeners
    
    129 124
         }
    
    130 125
     
    
    131
    -    override fun layoutDependsOn(parent: CoordinatorLayout, child: BrowserToolbar, dependency: View): Boolean {
    
    132
    -        if (toolbarPosition == ToolbarPosition.BOTTOM && dependency is Snackbar.SnackbarLayout) {
    
    133
    -            positionSnackbar(child, dependency)
    
    134
    -        }
    
    135
    -
    
    136
    -        return super.layoutDependsOn(parent, child, dependency)
    
    137
    -    }
    
    138
    -
    
    139 126
         override fun onLayoutChild(
    
    140 127
             parent: CoordinatorLayout,
    
    141 128
             child: BrowserToolbar,
    
    ... ... @@ -179,23 +166,6 @@ class BrowserToolbarBehavior(
    179 166
             isScrollEnabled = false
    
    180 167
         }
    
    181 168
     
    
    182
    -    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    
    183
    -    internal fun positionSnackbar(child: View, snackbarLayout: Snackbar.SnackbarLayout) {
    
    184
    -        val params = snackbarLayout.layoutParams as CoordinatorLayout.LayoutParams
    
    185
    -
    
    186
    -        // Position the snackbar above the toolbar so that it doesn't overlay the toolbar.
    
    187
    -        params.anchorId = child.id
    
    188
    -        params.anchorGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
    
    189
    -        params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
    
    190
    -
    
    191
    -        snackbarLayout.layoutParams = params
    
    192
    -
    
    193
    -        // In order to avoid the snackbar casting a shadow on the toolbar we adjust the elevation of the snackbar here.
    
    194
    -        // We still place it slightly behind the toolbar so that it will not animate over the toolbar but instead pop
    
    195
    -        // out from under the toolbar.
    
    196
    -        snackbarLayout.elevation = child.elevation - SMALL_ELEVATION_CHANGE
    
    197
    -    }
    
    198
    -
    
    199 169
         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    
    200 170
         internal fun tryToScrollVertically(distance: Float) {
    
    201 171
             browserToolbar?.let { toolbar ->
    

  • components/browser/toolbar/src/test/java/mozilla/components/browser/toolbar/behavior/BrowserToolbarBehaviorTest.kt
    ... ... @@ -6,14 +6,12 @@ package mozilla.components.browser.toolbar.behavior
    6 6
     
    
    7 7
     import android.content.Context
    
    8 8
     import android.graphics.Bitmap
    
    9
    -import android.view.Gravity
    
    10 9
     import android.view.MotionEvent.ACTION_DOWN
    
    11 10
     import android.view.MotionEvent.ACTION_MOVE
    
    12 11
     import android.widget.FrameLayout
    
    13 12
     import androidx.coordinatorlayout.widget.CoordinatorLayout
    
    14 13
     import androidx.core.view.ViewCompat
    
    15 14
     import androidx.test.ext.junit.runners.AndroidJUnit4
    
    16
    -import com.google.android.material.snackbar.Snackbar
    
    17 15
     import mozilla.components.browser.toolbar.BrowserToolbar
    
    18 16
     import mozilla.components.concept.engine.EngineSession
    
    19 17
     import mozilla.components.concept.engine.EngineView
    
    ... ... @@ -474,29 +472,6 @@ class BrowserToolbarBehaviorTest {
    474 472
             verify(yTranslator).collapseWithAnimation(toolbar)
    
    475 473
         }
    
    476 474
     
    
    477
    -    @Test
    
    478
    -    fun `Behavior will position snackbar above toolbar`() {
    
    479
    -        val behavior = BrowserToolbarBehavior(testContext, null, ToolbarPosition.BOTTOM)
    
    480
    -
    
    481
    -        val toolbar: BrowserToolbar = mock()
    
    482
    -        doReturn(4223).`when`(toolbar).id
    
    483
    -
    
    484
    -        val layoutParams: CoordinatorLayout.LayoutParams = CoordinatorLayout.LayoutParams(0, 0)
    
    485
    -
    
    486
    -        val snackbarLayout: Snackbar.SnackbarLayout = mock()
    
    487
    -        doReturn(layoutParams).`when`(snackbarLayout).layoutParams
    
    488
    -
    
    489
    -        behavior.layoutDependsOn(
    
    490
    -            parent = mock(),
    
    491
    -            child = toolbar,
    
    492
    -            dependency = snackbarLayout
    
    493
    -        )
    
    494
    -
    
    495
    -        assertEquals(4223, layoutParams.anchorId)
    
    496
    -        assertEquals(Gravity.TOP or Gravity.CENTER_HORIZONTAL, layoutParams.anchorGravity)
    
    497
    -        assertEquals(Gravity.TOP or Gravity.CENTER_HORIZONTAL, layoutParams.gravity)
    
    498
    -    }
    
    499
    -
    
    500 475
         @Test
    
    501 476
         fun `Behavior will forceExpand when scrolling up and !shouldScroll if the touch was handled in the browser`() {
    
    502 477
             val behavior = spy(BrowserToolbarBehavior(testContext, null, ToolbarPosition.BOTTOM))
    

  • components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadDialogFragment.kt
    ... ... @@ -10,7 +10,7 @@ import mozilla.components.browser.state.state.content.DownloadState
    10 10
     import mozilla.components.feature.downloads.DownloadDialogFragment.Companion.BYTES_TO_MB_LIMIT
    
    11 11
     import mozilla.components.feature.downloads.DownloadDialogFragment.Companion.KILOBYTE
    
    12 12
     import mozilla.components.feature.downloads.DownloadDialogFragment.Companion.MEGABYTE
    
    13
    -import mozilla.components.support.utils.DownloadUtils
    
    13
    +import mozilla.components.feature.downloads.ext.realFilenameOrGuessed
    
    14 14
     
    
    15 15
     /**
    
    16 16
      * This is a general representation of a dialog meant to be used in collaboration with [DownloadsFeature]
    
    ... ... @@ -34,11 +34,7 @@ abstract class DownloadDialogFragment : AppCompatDialogFragment() {
    34 34
          */
    
    35 35
         fun setDownload(download: DownloadState) {
    
    36 36
             val args = arguments ?: Bundle()
    
    37
    -        args.putString(
    
    38
    -            KEY_FILE_NAME,
    
    39
    -            download.fileName
    
    40
    -                ?: DownloadUtils.guessFileName(null, download.destinationDirectory, download.url, download.contentType)
    
    41
    -        )
    
    37
    +        args.putString(KEY_FILE_NAME, download.realFilenameOrGuessed)
    
    42 38
             args.putString(KEY_URL, download.url)
    
    43 39
             args.putLong(KEY_CONTENT_LENGTH, download.contentLength ?: 0)
    
    44 40
             arguments = args
    

  • components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt
    ... ... @@ -25,6 +25,7 @@ import mozilla.components.browser.state.state.SessionState
    25 25
     import mozilla.components.browser.state.state.content.DownloadState
    
    26 26
     import mozilla.components.browser.state.store.BrowserStore
    
    27 27
     import mozilla.components.feature.downloads.DownloadDialogFragment.Companion.FRAGMENT_TAG
    
    28
    +import mozilla.components.feature.downloads.ext.realFilenameOrGuessed
    
    28 29
     import mozilla.components.feature.downloads.manager.DownloadManager
    
    29 30
     import mozilla.components.feature.downloads.manager.noop
    
    30 31
     import mozilla.components.feature.downloads.manager.onDownloadStopped
    
    ... ... @@ -41,6 +42,43 @@ import mozilla.components.support.ktx.kotlin.isSameOriginAs
    41 42
     import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
    
    42 43
     import mozilla.components.support.utils.Browsers
    
    43 44
     
    
    45
    +/**
    
    46
    + * The name of the file to be downloaded.
    
    47
    + */
    
    48
    +@JvmInline
    
    49
    +value class Filename(val value: String)
    
    50
    +
    
    51
    +/**
    
    52
    + * The size of the file to be downloaded expressed as the number of `bytes`.
    
    53
    + * The value will be `0` if the size is unknown.
    
    54
    + */
    
    55
    +@JvmInline
    
    56
    +value class ContentSize(val value: Long)
    
    57
    +
    
    58
    +/**
    
    59
    + * The list of all applications that can perform a download, including this application.
    
    60
    + */
    
    61
    +@JvmInline
    
    62
    +value class ThirdPartyDownloaderApps(val value: List<DownloaderApp>)
    
    63
    +
    
    64
    +/**
    
    65
    + * Callback for when the user picked a certain application with which to download the current file.
    
    66
    + */
    
    67
    +@JvmInline
    
    68
    +value class ThirdPartyDownloaderAppChosenCallback(val value: (DownloaderApp) -> Unit)
    
    69
    +
    
    70
    +/**
    
    71
    + * Callback for when the positive button of a download dialog was tapped.
    
    72
    + */
    
    73
    +@JvmInline
    
    74
    +value class PositiveActionCallback(val value: () -> Unit)
    
    75
    +
    
    76
    +/**
    
    77
    + * Callback for when the negative button of a download dialog was tapped.
    
    78
    + */
    
    79
    +@JvmInline
    
    80
    +value class NegativeActionCallback(val value: () -> Unit)
    
    81
    +
    
    44 82
     /**
    
    45 83
      * Feature implementation to provide download functionality for the selected
    
    46 84
      * session. The feature will subscribe to the selected session and listen
    
    ... ... @@ -60,6 +98,10 @@ import mozilla.components.support.utils.Browsers
    60 98
      * @property promptsStyling styling properties for the dialog.
    
    61 99
      * @property shouldForwardToThirdParties Indicates if downloads should be forward to third party apps,
    
    62 100
      * if there are multiple apps a chooser dialog will shown.
    
    101
    + * @property customFirstPartyDownloadDialog An optional delegate for showing a dialog for a download
    
    102
    + * that will be processed by the current application.
    
    103
    + * @property customThirdPartyDownloadDialog An optional delegate for showing a dialog for a download
    
    104
    + * that can be processed by multiple installed applications including the current one.
    
    63 105
      */
    
    64 106
     @Suppress("LongParameterList", "LargeClass")
    
    65 107
     class DownloadsFeature(
    
    ... ... @@ -73,7 +115,11 @@ class DownloadsFeature(
    73 115
         private val tabId: String? = null,
    
    74 116
         private val fragmentManager: FragmentManager? = null,
    
    75 117
         private val promptsStyling: PromptsStyling? = null,
    
    76
    -    private val shouldForwardToThirdParties: () -> Boolean = { false }
    
    118
    +    private val shouldForwardToThirdParties: () -> Boolean = { false },
    
    119
    +    private val customFirstPartyDownloadDialog:
    
    120
    +    ((Filename, ContentSize, PositiveActionCallback, NegativeActionCallback) -> Unit)? = null,
    
    121
    +    private val customThirdPartyDownloadDialog:
    
    122
    +    ((ThirdPartyDownloaderApps, ThirdPartyDownloaderAppChosenCallback, NegativeActionCallback) -> Unit)? = null,
    
    77 123
     ) : LifecycleAwareFeature, PermissionsFeature {
    
    78 124
     
    
    79 125
         var onDownloadStopped: onDownloadStopped
    
    ... ... @@ -159,16 +205,45 @@ class DownloadsFeature(
    159 205
             val shouldShowAppDownloaderDialog = shouldForwardToThirdParties() && apps.size > 1
    
    160 206
     
    
    161 207
             return if (shouldShowAppDownloaderDialog) {
    
    162
    -            showAppDownloaderDialog(tab, download, apps)
    
    208
    +            when (customThirdPartyDownloadDialog) {
    
    209
    +                null -> showAppDownloaderDialog(tab, download, apps)
    
    210
    +                else -> customThirdPartyDownloadDialog.invoke(
    
    211
    +                    ThirdPartyDownloaderApps(apps),
    
    212
    +                    ThirdPartyDownloaderAppChosenCallback {
    
    213
    +                        onDownloaderAppSelected(it, tab, download)
    
    214
    +                    },
    
    215
    +                    NegativeActionCallback {
    
    216
    +                        useCases.cancelDownloadRequest.invoke(tab.id, download.id)
    
    217
    +                    },
    
    218
    +                )
    
    219
    +            }
    
    220
    +
    
    163 221
                 false
    
    164 222
             } else {
    
    165 223
                 if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) {
    
    166
    -                if (fragmentManager != null && !download.skipConfirmation) {
    
    167
    -                    showDownloadDialog(tab, download)
    
    168
    -                    false
    
    169
    -                } else {
    
    170
    -                    useCases.consumeDownload(tab.id, download.id)
    
    171
    -                    startDownload(download)
    
    224
    +                when {
    
    225
    +                    customFirstPartyDownloadDialog != null && !download.skipConfirmation -> {
    
    226
    +                        customFirstPartyDownloadDialog.invoke(
    
    227
    +                            Filename(download.realFilenameOrGuessed),
    
    228
    +                            ContentSize(download.contentLength ?: 0),
    
    229
    +                            PositiveActionCallback {
    
    230
    +                                startDownload(download)
    
    231
    +                                useCases.consumeDownload.invoke(tab.id, download.id)
    
    232
    +                            },
    
    233
    +                            NegativeActionCallback {
    
    234
    +                                useCases.cancelDownloadRequest.invoke(tab.id, download.id)
    
    235
    +                            },
    
    236
    +                        )
    
    237
    +                        false
    
    238
    +                    }
    
    239
    +                    fragmentManager != null && !download.skipConfirmation -> {
    
    240
    +                        showDownloadDialog(tab, download)
    
    241
    +                        false
    
    242
    +                    }
    
    243
    +                    else -> {
    
    244
    +                        useCases.consumeDownload(tab.id, download.id)
    
    245
    +                        startDownload(download)
    
    246
    +                    }
    
    172 247
                     }
    
    173 248
                 } else {
    
    174 249
                     onNeedToRequestPermissions(downloadManager.permissions)
    
    ... ... @@ -264,25 +339,7 @@ class DownloadsFeature(
    264 339
         ) {
    
    265 340
             appChooserDialog.setApps(apps)
    
    266 341
             appChooserDialog.onAppSelected = { app ->
    
    267
    -            if (app.packageName == applicationContext.packageName) {
    
    268
    -                if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) {
    
    269
    -                    startDownload(download)
    
    270
    -                    useCases.consumeDownload(tab.id, download.id)
    
    271
    -                } else {
    
    272
    -                    onNeedToRequestPermissions(downloadManager.permissions)
    
    273
    -                }
    
    274
    -            } else {
    
    275
    -                try {
    
    276
    -                    applicationContext.startActivity(app.toIntent())
    
    277
    -                } catch (error: ActivityNotFoundException) {
    
    278
    -                    val errorMessage = applicationContext.getString(
    
    279
    -                        R.string.mozac_feature_downloads_unable_to_open_third_party_app,
    
    280
    -                        app.name
    
    281
    -                    )
    
    282
    -                    Toast.makeText(applicationContext, errorMessage, Toast.LENGTH_SHORT).show()
    
    283
    -                }
    
    284
    -                useCases.consumeDownload(tab.id, download.id)
    
    285
    -            }
    
    342
    +            onDownloaderAppSelected(app, tab, download)
    
    286 343
             }
    
    287 344
     
    
    288 345
             appChooserDialog.onDismiss = {
    
    ... ... @@ -294,6 +351,29 @@ class DownloadsFeature(
    294 351
             }
    
    295 352
         }
    
    296 353
     
    
    354
    +    @VisibleForTesting
    
    355
    +    internal fun onDownloaderAppSelected(app: DownloaderApp, tab: SessionState, download: DownloadState) {
    
    356
    +        if (app.packageName == applicationContext.packageName) {
    
    357
    +            if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) {
    
    358
    +                startDownload(download)
    
    359
    +                useCases.consumeDownload(tab.id, download.id)
    
    360
    +            } else {
    
    361
    +                onNeedToRequestPermissions(downloadManager.permissions)
    
    362
    +            }
    
    363
    +        } else {
    
    364
    +            try {
    
    365
    +                applicationContext.startActivity(app.toIntent())
    
    366
    +            } catch (error: ActivityNotFoundException) {
    
    367
    +                val errorMessage = applicationContext.getString(
    
    368
    +                    R.string.mozac_feature_downloads_unable_to_open_third_party_app,
    
    369
    +                    app.name,
    
    370
    +                )
    
    371
    +                Toast.makeText(applicationContext, errorMessage, Toast.LENGTH_SHORT).show()
    
    372
    +            }
    
    373
    +            useCases.consumeDownload(tab.id, download.id)
    
    374
    +        }
    
    375
    +    }
    
    376
    +
    
    297 377
         private fun getAppDownloaderDialog() = findPreviousAppDownloaderDialogFragment()
    
    298 378
             ?: DownloadAppChooserDialog.newInstance(
    
    299 379
                 promptsStyling?.gravity,
    

  • components/feature/downloads/src/main/java/mozilla/components/feature/downloads/ext/DownloadState.kt
    ... ... @@ -47,3 +47,6 @@ internal fun DownloadState.withResponse(headers: Headers, stream: InputStream?):
    47 47
             contentLength = contentLength ?: headers[CONTENT_LENGTH]?.toLongOrNull()
    
    48 48
         )
    
    49 49
     }
    
    50
    +
    
    51
    +internal val DownloadState.realFilenameOrGuessed
    
    52
    +    get() = fileName ?: DownloadUtils.guessFileName(null, destinationDirectory, url, contentType)

  • components/feature/downloads/src/main/java/mozilla/components/feature/downloads/ui/DownloaderAppAdapter.kt
    ... ... @@ -16,10 +16,10 @@ import mozilla.components.feature.downloads.R
    16 16
     /**
    
    17 17
      * An adapter for displaying the applications that can perform downloads.
    
    18 18
      */
    
    19
    -internal class DownloaderAppAdapter(
    
    19
    +class DownloaderAppAdapter(
    
    20 20
         context: Context,
    
    21 21
         private val apps: List<DownloaderApp>,
    
    22
    -    val onAppSelected: ((DownloaderApp) -> Unit)
    
    22
    +    val onAppSelected: ((DownloaderApp) -> Unit),
    
    23 23
     ) : RecyclerView.Adapter<DownloaderAppViewHolder>() {
    
    24 24
     
    
    25 25
         private val inflater = LayoutInflater.from(context)
    
    ... ... @@ -49,11 +49,14 @@ internal class DownloaderAppAdapter(
    49 49
     /**
    
    50 50
      * View holder for a [DownloaderApp] item.
    
    51 51
      */
    
    52
    -internal class DownloaderAppViewHolder(
    
    52
    +class DownloaderAppViewHolder(
    
    53 53
         itemView: View,
    
    54 54
         val nameLabel: TextView,
    
    55
    -    val iconImage: ImageView
    
    55
    +    val iconImage: ImageView,
    
    56 56
     ) : RecyclerView.ViewHolder(itemView) {
    
    57
    +    /**
    
    58
    +     * Show a certain downloader application in the current View.
    
    59
    +     */
    
    57 60
         fun bind(app: DownloaderApp, onAppSelected: ((DownloaderApp) -> Unit)) {
    
    58 61
             itemView.app = app
    
    59 62
             itemView.setOnClickListener {
    

  • components/feature/prompts/build.gradle
    ... ... @@ -31,6 +31,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
    31 31
     dependencies {
    
    32 32
         implementation project(':browser-state')
    
    33 33
         implementation project(':concept-engine')
    
    34
    +    implementation project(':feature-session')
    
    34 35
         implementation project(':lib-state')
    
    35 36
         implementation project(':support-ktx')
    
    36 37
         implementation project(':support-utils')
    
    ... ... @@ -46,6 +47,7 @@ dependencies {
    46 47
         testImplementation Dependencies.testing_coroutines
    
    47 48
         testImplementation Dependencies.testing_robolectric
    
    48 49
         testImplementation Dependencies.testing_mockito
    
    50
    +    testImplementation project(':feature-session')
    
    49 51
         testImplementation project(':support-test')
    
    50 52
         testImplementation project(':support-test-libstate')
    
    51 53
     
    

  • components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptFeature.kt
    ... ... @@ -71,6 +71,8 @@ import mozilla.components.feature.prompts.login.LoginExceptions
    71 71
     import mozilla.components.feature.prompts.login.LoginPicker
    
    72 72
     import mozilla.components.feature.prompts.share.DefaultShareDelegate
    
    73 73
     import mozilla.components.feature.prompts.share.ShareDelegate
    
    74
    +import mozilla.components.feature.session.SessionUseCases
    
    75
    +import mozilla.components.feature.session.SessionUseCases.ExitFullScreenUseCase
    
    74 76
     import mozilla.components.lib.state.ext.flowScoped
    
    75 77
     import mozilla.components.support.base.feature.ActivityResultHandler
    
    76 78
     import mozilla.components.support.base.feature.LifecycleAwareFeature
    
    ... ... @@ -111,6 +113,7 @@ internal const val FRAGMENT_TAG = "mozac_feature_prompt_dialog"
    111 113
      * @property fragmentManager The [FragmentManager] to be used when displaying
    
    112 114
      * a dialog (fragment).
    
    113 115
      * @property shareDelegate Delegate used to display share sheet.
    
    116
    + * @property exitFullscreenUsecase Usecase allowing to exit browser tabs' fullscreen mode.
    
    114 117
      * @property loginStorageDelegate Delegate used to access login storage. If null,
    
    115 118
      * 'save login'prompts will not be shown.
    
    116 119
      * @property isSaveLoginEnabled A callback invoked when a login prompt is triggered. If false,
    
    ... ... @@ -144,6 +147,7 @@ class PromptFeature private constructor(
    144 147
         private var customTabId: String?,
    
    145 148
         private val fragmentManager: FragmentManager,
    
    146 149
         private val shareDelegate: ShareDelegate,
    
    150
    +    private val exitFullscreenUsecase: ExitFullScreenUseCase = SessionUseCases(store).exitFullscreen,
    
    147 151
         override val creditCardValidationDelegate: CreditCardValidationDelegate? = null,
    
    148 152
         override val loginValidationDelegate: LoginValidationDelegate? = null,
    
    149 153
         private val isSaveLoginEnabled: () -> Boolean = { false },
    
    ... ... @@ -184,6 +188,7 @@ class PromptFeature private constructor(
    184 188
             customTabId: String? = null,
    
    185 189
             fragmentManager: FragmentManager,
    
    186 190
             shareDelegate: ShareDelegate = DefaultShareDelegate(),
    
    191
    +        exitFullscreenUsecase: ExitFullScreenUseCase = SessionUseCases(store).exitFullscreen,
    
    187 192
             creditCardValidationDelegate: CreditCardValidationDelegate? = null,
    
    188 193
             loginValidationDelegate: LoginValidationDelegate? = null,
    
    189 194
             isSaveLoginEnabled: () -> Boolean = { false },
    
    ... ... @@ -202,6 +207,7 @@ class PromptFeature private constructor(
    202 207
             customTabId = customTabId,
    
    203 208
             fragmentManager = fragmentManager,
    
    204 209
             shareDelegate = shareDelegate,
    
    210
    +        exitFullscreenUsecase = exitFullscreenUsecase,
    
    205 211
             creditCardValidationDelegate = creditCardValidationDelegate,
    
    206 212
             loginValidationDelegate = loginValidationDelegate,
    
    207 213
             isSaveLoginEnabled = isSaveLoginEnabled,
    
    ... ... @@ -222,6 +228,7 @@ class PromptFeature private constructor(
    222 228
             customTabId: String? = null,
    
    223 229
             fragmentManager: FragmentManager,
    
    224 230
             shareDelegate: ShareDelegate = DefaultShareDelegate(),
    
    231
    +        exitFullscreenUsecase: ExitFullScreenUseCase = SessionUseCases(store).exitFullscreen,
    
    225 232
             creditCardValidationDelegate: CreditCardValidationDelegate? = null,
    
    226 233
             loginValidationDelegate: LoginValidationDelegate? = null,
    
    227 234
             isSaveLoginEnabled: () -> Boolean = { false },
    
    ... ... @@ -240,6 +247,7 @@ class PromptFeature private constructor(
    240 247
             customTabId = customTabId,
    
    241 248
             fragmentManager = fragmentManager,
    
    242 249
             shareDelegate = shareDelegate,
    
    250
    +        exitFullscreenUsecase = exitFullscreenUsecase,
    
    243 251
             creditCardValidationDelegate = creditCardValidationDelegate,
    
    244 252
             loginValidationDelegate = loginValidationDelegate,
    
    245 253
             isSaveLoginEnabled = isSaveLoginEnabled,
    
    ... ... @@ -420,6 +428,10 @@ class PromptFeature private constructor(
    420 428
         internal fun onPromptRequested(session: SessionState) {
    
    421 429
             // Some requests are handle with intents
    
    422 430
             session.content.promptRequests.lastOrNull()?.let { promptRequest ->
    
    431
    +            store.state.findTabOrCustomTabOrSelectedTab(customTabId)?.let {
    
    432
    +                exitFullscreenUsecase(it.id)
    
    433
    +            }
    
    434
    +
    
    423 435
                 when (promptRequest) {
    
    424 436
                     is File -> filePicker.handleFileRequest(promptRequest)
    
    425 437
                     is Share -> handleShareRequest(promptRequest, session)