From dcb7d2b354e07d75def865f9f2674278e26e224f Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Mon, 25 May 2020 11:50:15 +0100 Subject: [PATCH 1/2] Check for DownloadManager being enabled before downloading file --- .../app/browser/BrowserTabFragment.kt | 25 ++++++++++++++++++- .../browser/downloader/DataUriDownloader.kt | 4 +-- .../FileDownloadNotificationManager.kt | 1 - .../app/browser/downloader/FileDownloader.kt | 19 +++++++++++--- .../downloader/NetworkFileDownloader.kt | 23 ++++++++++++++++- .../main/res/values/string-untranslated.xml | 6 +++++ 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index cce88f67023b..118ee8a02140 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -29,6 +29,7 @@ import android.content.res.Configuration import android.media.MediaScannerConnection import android.net.Uri import android.os.* +import android.provider.Settings import android.text.Editable import android.view.* import android.view.View.* @@ -63,6 +64,7 @@ import com.duckduckgo.app.brokensite.BrokenSiteActivity import com.duckduckgo.app.brokensite.BrokenSiteData import com.duckduckgo.app.browser.BrowserTabViewModel.* import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter +import com.duckduckgo.app.browser.downloader.DownloadFailReason import com.duckduckgo.app.browser.downloader.FileDownloadNotificationManager import com.duckduckgo.app.browser.downloader.FileDownloader import com.duckduckgo.app.browser.downloader.FileDownloader.FileDownloadListener @@ -1112,9 +1114,30 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi } } - override fun downloadFailed(message: String) { + override fun downloadFailed(message: String, downloadFailReason: DownloadFailReason) { Timber.w("Failed to download file [$message]") + fileDownloadNotificationManager.showDownloadFailedNotification() + + val snackbar = Snackbar.make(toolbar, R.string.downloadFailed, Snackbar.LENGTH_INDEFINITE) + if (downloadFailReason == DownloadFailReason.DownloadManagerDisabled) { + snackbar.setText(message) + snackbar.setAction(getString(R.string.enable)) { + showDownloadManagerAppSettings() + } + } + snackbar.show() + } + + private fun showDownloadManagerAppSettings() { + try { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = DownloadFailReason.DOWNLOAD_MANAGER_SETTINGS_URI + startActivity(intent) + } catch (e: ActivityNotFoundException) { + Timber.w(e, "Could not open DownloadManager settings") + Snackbar.make(toolbar, R.string.downloadManagerIncompatible, Snackbar.LENGTH_INDEFINITE).show() + } } } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/downloader/DataUriDownloader.kt b/app/src/main/java/com/duckduckgo/app/browser/downloader/DataUriDownloader.kt index 599a15717f7f..57fb67a95266 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/downloader/DataUriDownloader.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/downloader/DataUriDownloader.kt @@ -38,7 +38,7 @@ class DataUriDownloader @Inject constructor( when (val parsedDataUri = dataUriParser.generate(pending.url)) { is ParseResult.Invalid -> { Timber.w("Failed to extract data from data URI") - callback?.downloadFailed("Failed to extract data from data URI") + callback?.downloadFailed("Failed to extract data from data URI", DownloadFailReason.DataUriParseException) return } is ParseResult.ParsedDataUri -> { @@ -50,7 +50,7 @@ class DataUriDownloader @Inject constructor( } } catch (e: IOException) { Timber.e(e, "Failed to save data uri") - callback?.downloadFailed("Failed to download data uri") + callback?.downloadFailed("Failed to download data uri", DownloadFailReason.DataUriParseException) } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloadNotificationManager.kt b/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloadNotificationManager.kt index 03e5ed626366..02261e81b135 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloadNotificationManager.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloadNotificationManager.kt @@ -70,7 +70,6 @@ class FileDownloadNotificationManager @Inject constructor( fun showDownloadFailedNotification() { applicationContext.runOnUiThread { - applicationContext.longToast(getString(R.string.downloadFailed)) val notification = NotificationCompat.Builder(applicationContext, ChannelType.FILE_DOWNLOADED.id) .setContentTitle(applicationContext.getString(R.string.downloadFailed)) diff --git a/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloader.kt b/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloader.kt index 9b42062c45d8..2473b1b6beb3 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloader.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/downloader/FileDownloader.kt @@ -16,6 +16,7 @@ package com.duckduckgo.app.browser.downloader +import android.net.Uri import android.os.Environment import android.webkit.URLUtil import androidx.annotation.WorkerThread @@ -32,9 +33,9 @@ class FileDownloader @Inject constructor( @WorkerThread fun download(pending: PendingFileDownload, callback: FileDownloadListener) { when { - pending.isNetworkUrl -> networkFileDownloader.download(pending) + pending.isNetworkUrl -> networkFileDownloader.download(pending, callback) pending.isDataUrl -> dataUriDownloader.download(pending, callback) - else -> callback.downloadFailed("Not supported") + else -> callback.downloadFailed("Not supported", DownloadFailReason.UnsupportedUrlType) } } @@ -50,7 +51,7 @@ class FileDownloader @Inject constructor( interface FileDownloadListener { fun downloadStarted() fun downloadFinished(file: File, mimeType: String?) - fun downloadFailed(message: String) + fun downloadFailed(message: String, downloadFailReason: DownloadFailReason) } } @@ -63,3 +64,15 @@ fun FileDownloader.PendingFileDownload.guessFileName(): String { val FileDownloader.PendingFileDownload.isDataUrl get() = URLUtil.isDataUrl(url) val FileDownloader.PendingFileDownload.isNetworkUrl get() = URLUtil.isNetworkUrl(url) + +sealed class DownloadFailReason { + + object DownloadManagerDisabled : DownloadFailReason() + object UnsupportedUrlType : DownloadFailReason() + object Other : DownloadFailReason() + object DataUriParseException : DownloadFailReason() + + companion object { + val DOWNLOAD_MANAGER_SETTINGS_URI: Uri = Uri.parse("package:com.android.providers.downloads") + } +} diff --git a/app/src/main/java/com/duckduckgo/app/browser/downloader/NetworkFileDownloader.kt b/app/src/main/java/com/duckduckgo/app/browser/downloader/NetworkFileDownloader.kt index 169961f06954..a976d0c039a4 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/downloader/NetworkFileDownloader.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/downloader/NetworkFileDownloader.kt @@ -18,14 +18,22 @@ package com.duckduckgo.app.browser.downloader import android.app.DownloadManager import android.content.Context +import android.content.pm.PackageManager.* import android.webkit.CookieManager import androidx.core.net.toUri +import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.downloader.FileDownloader.PendingFileDownload import javax.inject.Inject class NetworkFileDownloader @Inject constructor(private val context: Context) { - fun download(pendingDownload: PendingFileDownload) { + fun download(pendingDownload: PendingFileDownload, callback: FileDownloader.FileDownloadListener) { + + if (!downloadManagerAvailable()) { + callback.downloadFailed(context.getString(R.string.downloadManagerDisabled), DownloadFailReason.DownloadManagerDisabled) + return + } + val guessedFileName = pendingDownload.guessFileName() val request = DownloadManager.Request(pendingDownload.url.toUri()).apply { @@ -39,4 +47,17 @@ class NetworkFileDownloader @Inject constructor(private val context: Context) { val manager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager? manager?.enqueue(request) } + + private fun downloadManagerAvailable(): Boolean { + return when (context.packageManager.getApplicationEnabledSetting(DOWNLOAD_MANAGER_PACKAGE)) { + COMPONENT_ENABLED_STATE_DISABLED -> false + COMPONENT_ENABLED_STATE_DISABLED_USER -> false + COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED -> false + else -> true + } + } + + companion object { + private const val DOWNLOAD_MANAGER_PACKAGE = "com.android.providers.downloads" + } } diff --git a/app/src/main/res/values/string-untranslated.xml b/app/src/main/res/values/string-untranslated.xml index e2171ac54d50..8d2899e7ce30 100644 --- a/app/src/main/res/values/string-untranslated.xml +++ b/app/src/main/res/values/string-untranslated.xml @@ -69,4 +69,10 @@ No websites fireproofed yet Websites rely on cookies to keep you signed in. When you Fireproof a site, cookies won\'t be erased and you\'ll stay signed in, even after using the Fire Button. More options for fireproof website %s + + + Download failed because Download Manager is disabled + Download Manager not available on this device + Enable + From 94ec025ef546f3e09d026ff763e9cd3c7292851f Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Mon, 25 May 2020 11:53:43 +0100 Subject: [PATCH 2/2] Update string to make it fit better in snackbar --- app/src/main/res/values/string-untranslated.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/string-untranslated.xml b/app/src/main/res/values/string-untranslated.xml index 8d2899e7ce30..ed1d0727add4 100644 --- a/app/src/main/res/values/string-untranslated.xml +++ b/app/src/main/res/values/string-untranslated.xml @@ -71,7 +71,7 @@ More options for fireproof website %s - Download failed because Download Manager is disabled + Download Manager is disabled Download Manager not available on this device Enable