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..ed1d0727add4 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 Manager is disabled
+ Download Manager not available on this device
+ Enable
+