Bug 1867717 - Add DownloadsFeature to AddonPopupBaseFragment.
parent
851afc8745
commit
5fc19e6755
@ -0,0 +1,62 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.browser
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import mozilla.components.browser.state.state.SessionState
|
||||||
|
import mozilla.components.browser.state.state.content.DownloadState
|
||||||
|
import mozilla.components.browser.state.state.content.DownloadState.Status
|
||||||
|
import mozilla.components.feature.downloads.AbstractFetchDownloadService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for handling download dialogs.
|
||||||
|
*/
|
||||||
|
object DownloadDialogUtils {
|
||||||
|
|
||||||
|
internal fun handleOnDownloadFinished(
|
||||||
|
context: Context,
|
||||||
|
downloadState: DownloadState,
|
||||||
|
downloadJobStatus: Status,
|
||||||
|
currentTab: SessionState?,
|
||||||
|
onFinishedDialogShown: () -> Unit = {},
|
||||||
|
onCannotOpenFile: (DownloadState) -> Unit,
|
||||||
|
) {
|
||||||
|
// If the download is just paused, don't show any in-app notification
|
||||||
|
if (shouldShowCompletedDownloadDialog(downloadState, downloadJobStatus, currentTab)) {
|
||||||
|
if (downloadState.openInApp && downloadJobStatus == Status.COMPLETED) {
|
||||||
|
val fileWasOpened = openFile(context, downloadState)
|
||||||
|
if (!fileWasOpened) {
|
||||||
|
onCannotOpenFile(downloadState)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onFinishedDialogShown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
internal var openFile: (Context, DownloadState) -> (Boolean) = { context, downloadState ->
|
||||||
|
AbstractFetchDownloadService.openFile(
|
||||||
|
applicationContext = context.applicationContext,
|
||||||
|
download = downloadState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not a completed download dialog should be shown.
|
||||||
|
*/
|
||||||
|
fun shouldShowCompletedDownloadDialog(
|
||||||
|
downloadState: DownloadState,
|
||||||
|
status: Status,
|
||||||
|
currentTab: SessionState?,
|
||||||
|
): Boolean {
|
||||||
|
val isValidStatus =
|
||||||
|
status in listOf(Status.COMPLETED, Status.FAILED)
|
||||||
|
val isSameTab = downloadState.sessionId == (currentTab?.id ?: false)
|
||||||
|
|
||||||
|
return isValidStatus && isSameTab
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.fenix.browser
|
|
||||||
|
|
||||||
import mozilla.components.browser.state.state.content.DownloadState
|
|
||||||
import mozilla.components.browser.state.state.content.DownloadState.Status
|
|
||||||
import mozilla.components.feature.downloads.AbstractFetchDownloadService
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.downloads.DynamicDownloadDialog
|
|
||||||
|
|
||||||
internal fun BaseBrowserFragment.handleOnDownloadFinished(
|
|
||||||
downloadState: DownloadState,
|
|
||||||
downloadJobStatus: Status,
|
|
||||||
tryAgain: (String) -> Unit,
|
|
||||||
) {
|
|
||||||
// If the download is just paused, don't show any in-app notification
|
|
||||||
if (shouldShowCompletedDownloadDialog(downloadState, downloadJobStatus)) {
|
|
||||||
val safeContext = context ?: return
|
|
||||||
val onCannotOpenFile: (DownloadState) -> Unit = {
|
|
||||||
showCannotOpenFileError(binding.dynamicSnackbarContainer, safeContext, it)
|
|
||||||
}
|
|
||||||
if (downloadState.openInApp && downloadJobStatus == Status.COMPLETED) {
|
|
||||||
val fileWasOpened = AbstractFetchDownloadService.openFile(
|
|
||||||
applicationContext = safeContext.applicationContext,
|
|
||||||
download = downloadState,
|
|
||||||
)
|
|
||||||
if (!fileWasOpened) {
|
|
||||||
onCannotOpenFile(downloadState)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
saveDownloadDialogState(
|
|
||||||
downloadState.sessionId,
|
|
||||||
downloadState,
|
|
||||||
downloadJobStatus,
|
|
||||||
)
|
|
||||||
|
|
||||||
val dynamicDownloadDialog = DynamicDownloadDialog(
|
|
||||||
context = safeContext,
|
|
||||||
downloadState = downloadState,
|
|
||||||
didFail = downloadJobStatus == Status.FAILED,
|
|
||||||
tryAgain = tryAgain,
|
|
||||||
onCannotOpenFile = onCannotOpenFile,
|
|
||||||
binding = binding.viewDynamicDownloadDialog,
|
|
||||||
toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height),
|
|
||||||
) { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) }
|
|
||||||
|
|
||||||
dynamicDownloadDialog.show()
|
|
||||||
browserToolbarView.expand()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.Gravity
|
||||||
|
import mozilla.components.feature.downloads.DownloadsFeature
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.theme.ThemeManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to all Fenix download styling.
|
||||||
|
*/
|
||||||
|
object DownloadStyling {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates [DownloadsFeature.PromptsStyling].
|
||||||
|
*/
|
||||||
|
fun createPrompt(context: Context): DownloadsFeature.PromptsStyling {
|
||||||
|
return DownloadsFeature.PromptsStyling(
|
||||||
|
gravity = Gravity.BOTTOM,
|
||||||
|
shouldWidthMatchParent = true,
|
||||||
|
positiveButtonBackgroundColor = ThemeManager.resolveAttribute(
|
||||||
|
R.attr.accent,
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
positiveButtonTextColor = ThemeManager.resolveAttribute(
|
||||||
|
R.attr.textOnColorPrimary,
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
positiveButtonRadius = (context.resources.getDimensionPixelSize(R.dimen.tab_corner_radius)).toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.addons
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.spyk
|
||||||
|
import io.mockk.verify
|
||||||
|
import mozilla.components.browser.state.action.ContentAction
|
||||||
|
import mozilla.components.browser.state.state.SessionState
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.concept.fetch.Response
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
|
|
||||||
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
|
class AddonPopupBaseFragmentTest {
|
||||||
|
|
||||||
|
private lateinit var fragment: AddonPopupBaseFragment
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
fragment = spyk()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN onExternalResource is call THEN dispatch an UpdateDownloadAction`() {
|
||||||
|
val store = mockk<BrowserStore>(relaxed = true)
|
||||||
|
val session = mockk<SessionState>(relaxed = true)
|
||||||
|
val response = mockk<Response>(relaxed = true)
|
||||||
|
every { fragment.provideBrowserStore() } returns store
|
||||||
|
|
||||||
|
fragment.session = session
|
||||||
|
|
||||||
|
fragment.onExternalResource(
|
||||||
|
url = "url",
|
||||||
|
fileName = "fileName",
|
||||||
|
contentLength = 1,
|
||||||
|
contentType = "contentType",
|
||||||
|
userAgent = "userAgent",
|
||||||
|
isPrivate = true,
|
||||||
|
skipConfirmation = false,
|
||||||
|
openInApp = false,
|
||||||
|
response = response,
|
||||||
|
)
|
||||||
|
|
||||||
|
verify { store.dispatch(any<ContentAction.UpdateDownloadAction>()) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.browser
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import mozilla.components.browser.state.state.content.DownloadState
|
||||||
|
import mozilla.components.browser.state.state.content.DownloadState.Status.COMPLETED
|
||||||
|
import mozilla.components.browser.state.state.content.DownloadState.Status.FAILED
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class DownloadDialogUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
fun `GIVEN final status WHEN handleOnDownloadFinished try to open the file fails THEN show error message`() {
|
||||||
|
val downloadUtils = DownloadDialogUtils
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = currentTab.id,
|
||||||
|
destinationDirectory = "/",
|
||||||
|
openInApp = true,
|
||||||
|
)
|
||||||
|
var dialogWasShown = false
|
||||||
|
var onCannotOpenFileWasCalled = false
|
||||||
|
|
||||||
|
downloadUtils.openFile = { _, _ -> false }
|
||||||
|
|
||||||
|
downloadUtils.handleOnDownloadFinished(
|
||||||
|
context = testContext,
|
||||||
|
downloadState = download,
|
||||||
|
downloadJobStatus = COMPLETED,
|
||||||
|
currentTab = currentTab,
|
||||||
|
onFinishedDialogShown = { dialogWasShown = true },
|
||||||
|
onCannotOpenFile = {
|
||||||
|
onCannotOpenFileWasCalled = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(onCannotOpenFileWasCalled)
|
||||||
|
assertFalse(dialogWasShown)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN final status and openInApp WHEN calling handleOnDownloadFinished THEN try to open the file`() {
|
||||||
|
val downloadUtils = DownloadDialogUtils
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = "1",
|
||||||
|
destinationDirectory = "/",
|
||||||
|
openInApp = true,
|
||||||
|
)
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
var dialogWasShown = false
|
||||||
|
var onCannotOpenFileWasCalled = false
|
||||||
|
|
||||||
|
downloadUtils.openFile = { _, _ -> true }
|
||||||
|
|
||||||
|
downloadUtils.handleOnDownloadFinished(
|
||||||
|
context = testContext,
|
||||||
|
downloadState = download,
|
||||||
|
downloadJobStatus = COMPLETED,
|
||||||
|
currentTab = currentTab,
|
||||||
|
onFinishedDialogShown = { dialogWasShown = true },
|
||||||
|
onCannotOpenFile = {
|
||||||
|
onCannotOpenFileWasCalled = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assertFalse(onCannotOpenFileWasCalled)
|
||||||
|
assertFalse(dialogWasShown)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN final status WHEN calling handleOnDownloadFinished THEN show a finished download dialog`() {
|
||||||
|
val downloadUtils = DownloadDialogUtils
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = "1",
|
||||||
|
destinationDirectory = "/",
|
||||||
|
)
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
var dialogWasShown = false
|
||||||
|
|
||||||
|
downloadUtils.handleOnDownloadFinished(
|
||||||
|
context = testContext,
|
||||||
|
downloadState = download,
|
||||||
|
downloadJobStatus = COMPLETED,
|
||||||
|
currentTab = currentTab,
|
||||||
|
onFinishedDialogShown = { dialogWasShown = true },
|
||||||
|
onCannotOpenFile = {},
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(dialogWasShown)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN status is final and is same tab THEN shouldShowCompletedDownloadDialog will be true`() {
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = "1",
|
||||||
|
destinationDirectory = "/",
|
||||||
|
)
|
||||||
|
|
||||||
|
val status = DownloadState.Status.values().filter { it == COMPLETED || it == FAILED }
|
||||||
|
|
||||||
|
status.forEach {
|
||||||
|
val result = DownloadDialogUtils.shouldShowCompletedDownloadDialog(
|
||||||
|
downloadState = download,
|
||||||
|
status = it,
|
||||||
|
currentTab = currentTab,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN status is different from FAILED or COMPLETED then shouldShowCompletedDownloadDialog will be false`() {
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = "1",
|
||||||
|
destinationDirectory = "/",
|
||||||
|
)
|
||||||
|
|
||||||
|
val completedStatus = listOf(COMPLETED, FAILED)
|
||||||
|
val status = DownloadState.Status.values().filter { it !in completedStatus }
|
||||||
|
|
||||||
|
status.forEach {
|
||||||
|
val result = DownloadDialogUtils.shouldShowCompletedDownloadDialog(
|
||||||
|
downloadState = download,
|
||||||
|
status = it,
|
||||||
|
currentTab = currentTab,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertFalse(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN the tab is different from the initial one then shouldShowCompletedDownloadDialog will be false`() {
|
||||||
|
val currentTab = createTab(id = "1", url = "")
|
||||||
|
|
||||||
|
val download = DownloadState(
|
||||||
|
url = "",
|
||||||
|
sessionId = "2",
|
||||||
|
destinationDirectory = "/",
|
||||||
|
)
|
||||||
|
val completedStatus = listOf(COMPLETED, FAILED)
|
||||||
|
val status = DownloadState.Status.values().filter { it !in completedStatus }
|
||||||
|
status.forEach {
|
||||||
|
val result =
|
||||||
|
DownloadDialogUtils.shouldShowCompletedDownloadDialog(
|
||||||
|
downloadState = download,
|
||||||
|
status = it,
|
||||||
|
currentTab = currentTab,
|
||||||
|
)
|
||||||
|
assertFalse(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue