Loading components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt +61 −26 Original line number Diff line number Diff line Loading @@ -56,13 +56,25 @@ value class Filename(val value: String) value class ContentSize(val value: Long) /** * Action for when the positive button of a download dialog was tapped. * The list of all applications that can perform a download, including this application. */ @JvmInline value class ThirdPartyDownloaderApps(val value: List<DownloaderApp>) /** * Callback for when the user picked a certain application with which to download the current file. */ @JvmInline value class ThirdPartyDownloaderAppChosenCallback(val value: (DownloaderApp) -> Unit) /** * Callback for when the positive button of a download dialog was tapped. */ @JvmInline value class PositiveActionCallback(val value: () -> Unit) /** * Action for when the negative button of a download dialog was tapped. * Callback for when the negative button of a download dialog was tapped. */ @JvmInline value class NegativeActionCallback(val value: () -> Unit) Loading @@ -86,7 +98,10 @@ value class NegativeActionCallback(val value: () -> Unit) * @property promptsStyling styling properties for the dialog. * @property shouldForwardToThirdParties Indicates if downloads should be forward to third party apps, * if there are multiple apps a chooser dialog will shown. * @property customDownloadDialog An optional delegate for showing a download dialog. * @property customFirstPartyDownloadDialog An optional delegate for showing a dialog for a download * that will be processed by the current application. * @property customThirdPartyDownloadDialog An optional delegate for showing a dialog for a download * that can be processed by multiple installed applications including the current one. */ @Suppress("LongParameterList", "LargeClass") class DownloadsFeature( Loading @@ -101,7 +116,10 @@ class DownloadsFeature( private val fragmentManager: FragmentManager? = null, private val promptsStyling: PromptsStyling? = null, private val shouldForwardToThirdParties: () -> Boolean = { false }, private val customDownloadDialog: ((Filename, ContentSize, PositiveActionCallback, NegativeActionCallback) -> Unit)? = null, private val customFirstPartyDownloadDialog: ((Filename, ContentSize, PositiveActionCallback, NegativeActionCallback) -> Unit)? = null, private val customThirdPartyDownloadDialog: ((ThirdPartyDownloaderApps, ThirdPartyDownloaderAppChosenCallback, NegativeActionCallback) -> Unit)? = null, ) : LifecycleAwareFeature, PermissionsFeature { var onDownloadStopped: onDownloadStopped Loading Loading @@ -187,13 +205,25 @@ class DownloadsFeature( val shouldShowAppDownloaderDialog = shouldForwardToThirdParties() && apps.size > 1 return if (shouldShowAppDownloaderDialog) { showAppDownloaderDialog(tab, download, apps) when (customThirdPartyDownloadDialog) { null -> showAppDownloaderDialog(tab, download, apps) else -> customThirdPartyDownloadDialog.invoke( ThirdPartyDownloaderApps(apps), ThirdPartyDownloaderAppChosenCallback { onDownloaderAppSelected(it, tab, download) }, NegativeActionCallback { useCases.cancelDownloadRequest.invoke(tab.id, download.id) }, ) } false } else { if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) { when { customDownloadDialog != null && !download.skipConfirmation -> { customDownloadDialog.invoke( customFirstPartyDownloadDialog != null && !download.skipConfirmation -> { customFirstPartyDownloadDialog.invoke( Filename(download.realFilenameOrGuessed), ContentSize(download.contentLength ?: 0), PositiveActionCallback { Loading Loading @@ -309,6 +339,20 @@ class DownloadsFeature( ) { appChooserDialog.setApps(apps) appChooserDialog.onAppSelected = { app -> onDownloaderAppSelected(app, tab, download) } appChooserDialog.onDismiss = { useCases.cancelDownloadRequest.invoke(tab.id, download.id) } if (!isAlreadyAppDownloaderDialog() && fragmentManager != null && !fragmentManager.isDestroyed) { appChooserDialog.showNow(fragmentManager, DownloadAppChooserDialog.FRAGMENT_TAG) } } @VisibleForTesting internal fun onDownloaderAppSelected(app: DownloaderApp, tab: SessionState, download: DownloadState) { if (app.packageName == applicationContext.packageName) { if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) { startDownload(download) Loading @@ -322,7 +366,7 @@ class DownloadsFeature( } catch (error: ActivityNotFoundException) { val errorMessage = applicationContext.getString( R.string.mozac_feature_downloads_unable_to_open_third_party_app, app.name app.name, ) Toast.makeText(applicationContext, errorMessage, Toast.LENGTH_SHORT).show() } Loading @@ -330,15 +374,6 @@ class DownloadsFeature( } } appChooserDialog.onDismiss = { useCases.cancelDownloadRequest.invoke(tab.id, download.id) } if (!isAlreadyAppDownloaderDialog() && fragmentManager != null && !fragmentManager.isDestroyed) { appChooserDialog.showNow(fragmentManager, DownloadAppChooserDialog.FRAGMENT_TAG) } } private fun getAppDownloaderDialog() = findPreviousAppDownloaderDialogFragment() ?: DownloadAppChooserDialog.newInstance( promptsStyling?.gravity, Loading components/feature/downloads/src/main/java/mozilla/components/feature/downloads/ui/DownloaderAppAdapter.kt +7 −4 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ import mozilla.components.feature.downloads.R /** * An adapter for displaying the applications that can perform downloads. */ internal class DownloaderAppAdapter( class DownloaderAppAdapter( context: Context, private val apps: List<DownloaderApp>, val onAppSelected: ((DownloaderApp) -> Unit) val onAppSelected: ((DownloaderApp) -> Unit), ) : RecyclerView.Adapter<DownloaderAppViewHolder>() { private val inflater = LayoutInflater.from(context) Loading Loading @@ -49,11 +49,14 @@ internal class DownloaderAppAdapter( /** * View holder for a [DownloaderApp] item. */ internal class DownloaderAppViewHolder( class DownloaderAppViewHolder( itemView: View, val nameLabel: TextView, val iconImage: ImageView val iconImage: ImageView, ) : RecyclerView.ViewHolder(itemView) { /** * Show a certain downloader application in the current View. */ fun bind(app: DownloaderApp, onAppSelected: ((DownloaderApp) -> Unit)) { itemView.app = app itemView.setOnClickListener { Loading Loading
components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt +61 −26 Original line number Diff line number Diff line Loading @@ -56,13 +56,25 @@ value class Filename(val value: String) value class ContentSize(val value: Long) /** * Action for when the positive button of a download dialog was tapped. * The list of all applications that can perform a download, including this application. */ @JvmInline value class ThirdPartyDownloaderApps(val value: List<DownloaderApp>) /** * Callback for when the user picked a certain application with which to download the current file. */ @JvmInline value class ThirdPartyDownloaderAppChosenCallback(val value: (DownloaderApp) -> Unit) /** * Callback for when the positive button of a download dialog was tapped. */ @JvmInline value class PositiveActionCallback(val value: () -> Unit) /** * Action for when the negative button of a download dialog was tapped. * Callback for when the negative button of a download dialog was tapped. */ @JvmInline value class NegativeActionCallback(val value: () -> Unit) Loading @@ -86,7 +98,10 @@ value class NegativeActionCallback(val value: () -> Unit) * @property promptsStyling styling properties for the dialog. * @property shouldForwardToThirdParties Indicates if downloads should be forward to third party apps, * if there are multiple apps a chooser dialog will shown. * @property customDownloadDialog An optional delegate for showing a download dialog. * @property customFirstPartyDownloadDialog An optional delegate for showing a dialog for a download * that will be processed by the current application. * @property customThirdPartyDownloadDialog An optional delegate for showing a dialog for a download * that can be processed by multiple installed applications including the current one. */ @Suppress("LongParameterList", "LargeClass") class DownloadsFeature( Loading @@ -101,7 +116,10 @@ class DownloadsFeature( private val fragmentManager: FragmentManager? = null, private val promptsStyling: PromptsStyling? = null, private val shouldForwardToThirdParties: () -> Boolean = { false }, private val customDownloadDialog: ((Filename, ContentSize, PositiveActionCallback, NegativeActionCallback) -> Unit)? = null, private val customFirstPartyDownloadDialog: ((Filename, ContentSize, PositiveActionCallback, NegativeActionCallback) -> Unit)? = null, private val customThirdPartyDownloadDialog: ((ThirdPartyDownloaderApps, ThirdPartyDownloaderAppChosenCallback, NegativeActionCallback) -> Unit)? = null, ) : LifecycleAwareFeature, PermissionsFeature { var onDownloadStopped: onDownloadStopped Loading Loading @@ -187,13 +205,25 @@ class DownloadsFeature( val shouldShowAppDownloaderDialog = shouldForwardToThirdParties() && apps.size > 1 return if (shouldShowAppDownloaderDialog) { showAppDownloaderDialog(tab, download, apps) when (customThirdPartyDownloadDialog) { null -> showAppDownloaderDialog(tab, download, apps) else -> customThirdPartyDownloadDialog.invoke( ThirdPartyDownloaderApps(apps), ThirdPartyDownloaderAppChosenCallback { onDownloaderAppSelected(it, tab, download) }, NegativeActionCallback { useCases.cancelDownloadRequest.invoke(tab.id, download.id) }, ) } false } else { if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) { when { customDownloadDialog != null && !download.skipConfirmation -> { customDownloadDialog.invoke( customFirstPartyDownloadDialog != null && !download.skipConfirmation -> { customFirstPartyDownloadDialog.invoke( Filename(download.realFilenameOrGuessed), ContentSize(download.contentLength ?: 0), PositiveActionCallback { Loading Loading @@ -309,6 +339,20 @@ class DownloadsFeature( ) { appChooserDialog.setApps(apps) appChooserDialog.onAppSelected = { app -> onDownloaderAppSelected(app, tab, download) } appChooserDialog.onDismiss = { useCases.cancelDownloadRequest.invoke(tab.id, download.id) } if (!isAlreadyAppDownloaderDialog() && fragmentManager != null && !fragmentManager.isDestroyed) { appChooserDialog.showNow(fragmentManager, DownloadAppChooserDialog.FRAGMENT_TAG) } } @VisibleForTesting internal fun onDownloaderAppSelected(app: DownloaderApp, tab: SessionState, download: DownloadState) { if (app.packageName == applicationContext.packageName) { if (applicationContext.isPermissionGranted(downloadManager.permissions.asIterable())) { startDownload(download) Loading @@ -322,7 +366,7 @@ class DownloadsFeature( } catch (error: ActivityNotFoundException) { val errorMessage = applicationContext.getString( R.string.mozac_feature_downloads_unable_to_open_third_party_app, app.name app.name, ) Toast.makeText(applicationContext, errorMessage, Toast.LENGTH_SHORT).show() } Loading @@ -330,15 +374,6 @@ class DownloadsFeature( } } appChooserDialog.onDismiss = { useCases.cancelDownloadRequest.invoke(tab.id, download.id) } if (!isAlreadyAppDownloaderDialog() && fragmentManager != null && !fragmentManager.isDestroyed) { appChooserDialog.showNow(fragmentManager, DownloadAppChooserDialog.FRAGMENT_TAG) } } private fun getAppDownloaderDialog() = findPreviousAppDownloaderDialogFragment() ?: DownloadAppChooserDialog.newInstance( promptsStyling?.gravity, Loading
components/feature/downloads/src/main/java/mozilla/components/feature/downloads/ui/DownloaderAppAdapter.kt +7 −4 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ import mozilla.components.feature.downloads.R /** * An adapter for displaying the applications that can perform downloads. */ internal class DownloaderAppAdapter( class DownloaderAppAdapter( context: Context, private val apps: List<DownloaderApp>, val onAppSelected: ((DownloaderApp) -> Unit) val onAppSelected: ((DownloaderApp) -> Unit), ) : RecyclerView.Adapter<DownloaderAppViewHolder>() { private val inflater = LayoutInflater.from(context) Loading Loading @@ -49,11 +49,14 @@ internal class DownloaderAppAdapter( /** * View holder for a [DownloaderApp] item. */ internal class DownloaderAppViewHolder( class DownloaderAppViewHolder( itemView: View, val nameLabel: TextView, val iconImage: ImageView val iconImage: ImageView, ) : RecyclerView.ViewHolder(itemView) { /** * Show a certain downloader application in the current View. */ fun bind(app: DownloaderApp, onAppSelected: ((DownloaderApp) -> Unit)) { itemView.app = app itemView.setOnClickListener { Loading