@@ -40,14 +40,18 @@ import android.webkit.WebView.HitTestResult
4040import android.webkit.WebView.HitTestResult.*
4141import android.widget.EditText
4242import android.widget.TextView
43+ import android.widget.Toast
4344import androidx.annotation.AnyThread
4445import androidx.annotation.StringRes
4546import androidx.appcompat.app.AlertDialog
4647import androidx.core.content.ContextCompat
48+ import androidx.core.content.FileProvider
49+ import androidx.core.net.toUri
4750import androidx.core.text.HtmlCompat
4851import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
4952import androidx.core.view.*
5053import androidx.fragment.app.Fragment
54+ import androidx.fragment.app.commitNow
5155import androidx.fragment.app.transaction
5256import androidx.lifecycle.*
5357import androidx.recyclerview.widget.LinearLayoutManager
@@ -56,6 +60,8 @@ import com.duckduckgo.app.bookmarks.ui.EditBookmarkDialogFragment
5660import com.duckduckgo.app.brokensite.BrokenSiteActivity
5761import com.duckduckgo.app.brokensite.BrokenSiteData
5862import com.duckduckgo.app.browser.BrowserTabViewModel.*
63+ import com.duckduckgo.app.browser.BrowserTabViewModel.Command.DownloadCommand
64+ import com.duckduckgo.app.browser.DownloadConfirmationFragment.DownloadConfirmationDialogListener
5965import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter
6066import com.duckduckgo.app.browser.downloader.DownloadFailReason
6167import com.duckduckgo.app.browser.downloader.FileDownloadNotificationManager
@@ -107,10 +113,9 @@ import org.jetbrains.anko.share
107113import timber.log.Timber
108114import java.io.File
109115import javax.inject.Inject
110- import kotlin.concurrent.thread
111116import kotlin.coroutines.CoroutineContext
112117
113- class BrowserTabFragment : Fragment (), FindListener, CoroutineScope, DaxDialogListener, TrackersAnimatorListener {
118+ class BrowserTabFragment : Fragment (), FindListener, CoroutineScope, DaxDialogListener, TrackersAnimatorListener, DownloadConfirmationDialogListener {
114119
115120 private val supervisorJob = SupervisorJob ()
116121
@@ -249,14 +254,6 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi
249254 removeDaxDialogFromActivity()
250255 renderer = BrowserTabFragmentRenderer ()
251256 decorator = BrowserTabFragmentDecorator ()
252- if (savedInstanceState != null ) {
253- updateFragmentListener()
254- }
255- }
256-
257- private fun updateFragmentListener () {
258- val fragment = fragmentManager?.findFragmentByTag(DOWNLOAD_CONFIRMATION_TAG ) as ? DownloadConfirmationFragment
259- fragment?.downloadListener = createDownloadListener()
260257 }
261258
262259 override fun onCreateView (inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ): View ? {
@@ -556,6 +553,35 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi
556553 is Command .ShowWebContent -> webView?.show()
557554 is Command .RefreshUserAgent -> refreshUserAgent(it.host, it.isDesktop)
558555 is Command .AskToFireproofWebsite -> askToFireproofWebsite(requireContext(), it.fireproofWebsite)
556+ is DownloadCommand -> processDownloadCommand(it)
557+ }
558+ }
559+
560+ private fun processDownloadCommand (it : DownloadCommand ) {
561+ when (it) {
562+ is DownloadCommand .ScanMediaFiles -> {
563+ context?.applicationContext?.let { context ->
564+ MediaScannerConnection .scanFile(context, arrayOf(it.file.absolutePath), null , null )
565+ }
566+ }
567+ is DownloadCommand .ShowDownloadFinishedNotification -> {
568+ fileDownloadNotificationManager.showDownloadFinishedNotification(it.file.name, it.file.absolutePath.toUri(), it.mimeType)
569+ }
570+ DownloadCommand .ShowDownloadInProgressNotification -> {
571+ fileDownloadNotificationManager.showDownloadInProgressNotification()
572+ }
573+ is DownloadCommand .ShowDownloadFailedNotification -> {
574+ fileDownloadNotificationManager.showDownloadFailedNotification()
575+
576+ val snackbar = Snackbar .make(toolbar, R .string.downloadFailed, Snackbar .LENGTH_INDEFINITE )
577+ if (it.reason == DownloadFailReason .DownloadManagerDisabled ) {
578+ snackbar.setText(it.message)
579+ snackbar.setAction(getString(R .string.enable)) {
580+ showDownloadManagerAppSettings()
581+ }
582+ }
583+ snackbar.show()
584+ }
559585 }
560586 }
561587
@@ -1092,90 +1118,28 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi
10921118
10931119 @AnyThread
10941120 private fun downloadFile (requestUserConfirmation : Boolean ) {
1095- val pendingDownload = pendingFileDownload
1096- pendingFileDownload = null
1121+ val pendingDownload = pendingFileDownload ? : return
10971122
1098- if (pendingDownload == null ) {
1099- return
1100- }
1123+ pendingFileDownload = null
11011124
1102- val downloadListener = createDownloadListener()
11031125 if (requestUserConfirmation) {
1104- requestDownloadConfirmation(pendingDownload, downloadListener )
1126+ requestDownloadConfirmation(pendingDownload)
11051127 } else {
1106- completeDownload (pendingDownload, downloadListener )
1128+ continueDownload (pendingDownload)
11071129 }
11081130 }
11091131
1110- private fun closeAndReturnToSourceIfBlankTab () {
1111- if (viewModel.url == null ) {
1112- launch {
1113- viewModel.closeAndSelectSourceTab()
1114- }
1115- }
1116- }
1117-
1118- private fun createDownloadListener (): FileDownloadListener {
1119- return object : FileDownloadListener {
1120- override fun downloadStarted () {
1121- fileDownloadNotificationManager.showDownloadInProgressNotification()
1122- closeAndReturnToSourceIfBlankTab()
1123- }
1124-
1125- override fun downloadFinished (file : File , mimeType : String? ) {
1126- MediaScannerConnection .scanFile(context, arrayOf(file.absolutePath), null ) { _, uri ->
1127- fileDownloadNotificationManager.showDownloadFinishedNotification(file.name, uri, mimeType)
1128- }
1129- }
1130-
1131- override fun downloadFailed (message : String , downloadFailReason : DownloadFailReason ) {
1132- Timber .w(" Failed to download file [$message ]" )
1133-
1134- fileDownloadNotificationManager.showDownloadFailedNotification()
1135-
1136- val snackbar = Snackbar .make(toolbar, R .string.downloadFailed, Snackbar .LENGTH_INDEFINITE )
1137- if (downloadFailReason == DownloadFailReason .DownloadManagerDisabled ) {
1138- snackbar.setText(message)
1139- snackbar.setAction(getString(R .string.enable)) {
1140- showDownloadManagerAppSettings()
1141- }
1142- }
1143- snackbar.show()
1144- }
1145-
1146- override fun downloadCancelled () {
1147- closeAndReturnToSourceIfBlankTab()
1148- }
1149-
1150- override fun downloadOpened () {
1151- closeAndReturnToSourceIfBlankTab()
1152- }
1153-
1154- private fun showDownloadManagerAppSettings () {
1155- try {
1156- val intent = Intent (Settings .ACTION_APPLICATION_DETAILS_SETTINGS )
1157- intent.data = DownloadFailReason .DOWNLOAD_MANAGER_SETTINGS_URI
1158- startActivity(intent)
1159- } catch (e: ActivityNotFoundException ) {
1160- Timber .w(e, " Could not open DownloadManager settings" )
1161- Snackbar .make(toolbar, R .string.downloadManagerIncompatible, Snackbar .LENGTH_INDEFINITE ).show()
1162- }
1163- }
1164- }
1165- }
1166-
1167- private fun requestDownloadConfirmation (pendingDownload : PendingFileDownload , downloadListener : FileDownloadListener ) {
1168- fragmentManager?.let {
1169- if (! it.isStateSaved) {
1170- DownloadConfirmationFragment .instance(pendingDownload, downloadListener).show(it, DOWNLOAD_CONFIRMATION_TAG )
1171- }
1132+ private fun requestDownloadConfirmation (pendingDownload : PendingFileDownload ) {
1133+ val downloadConfirmationFragment = DownloadConfirmationFragment .instance(pendingDownload)
1134+ childFragmentManager.findFragmentByTag(DOWNLOAD_CONFIRMATION_TAG )?.let {
1135+ Timber .i(" Found existing dialog; removing it now" )
1136+ childFragmentManager.commitNow { remove(it) }
11721137 }
1138+ downloadConfirmationFragment.show(childFragmentManager, DOWNLOAD_CONFIRMATION_TAG )
11731139 }
11741140
11751141 private fun completeDownload (pendingDownload : PendingFileDownload , callback : FileDownloadListener ) {
1176- thread {
1177- fileDownloader.download(pendingDownload, callback)
1178- }
1142+ viewModel.download(pendingDownload)
11791143 }
11801144
11811145 private fun launchFilePicker (command : Command .ShowFileChooser ) {
@@ -1787,4 +1751,55 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi
17871751 private fun shouldUpdateOmnibarTextInput (viewState : OmnibarViewState , omnibarInput : String? ) =
17881752 (! viewState.isEditing || omnibarInput.isNullOrEmpty()) && omnibarTextInput.isDifferent(omnibarInput)
17891753 }
1754+
1755+ override fun openExistingFile (file : File ? ) {
1756+ if (file == null ) {
1757+ Toast .makeText(activity, R .string.downloadConfirmationUnableToOpenFileText, Toast .LENGTH_SHORT ).show()
1758+ return
1759+ }
1760+
1761+ val intent = context?.let { createIntentToOpenFile(it, file) }
1762+ activity?.packageManager?.let { packageManager ->
1763+ if (intent?.resolveActivity(packageManager) != null ) {
1764+ startActivity(intent)
1765+ } else {
1766+ Timber .e(" No suitable activity found" )
1767+ Toast .makeText(activity, R .string.downloadConfirmationUnableToOpenFileText, Toast .LENGTH_SHORT ).show()
1768+ }
1769+ }
1770+ }
1771+
1772+ override fun replaceExistingFile (file : File ? , pendingFileDownload : PendingFileDownload ) {
1773+ Timber .i(" Deleting existing file: $file " )
1774+ runCatching { file?.delete() }
1775+ continueDownload(pendingFileDownload)
1776+ }
1777+
1778+ private fun showDownloadManagerAppSettings () {
1779+ try {
1780+ val intent = Intent (Settings .ACTION_APPLICATION_DETAILS_SETTINGS )
1781+ intent.data = DownloadFailReason .DOWNLOAD_MANAGER_SETTINGS_URI
1782+ startActivity(intent)
1783+ } catch (e: ActivityNotFoundException ) {
1784+ Timber .w(e, " Could not open DownloadManager settings" )
1785+ Snackbar .make(toolbar, R .string.downloadManagerIncompatible, Snackbar .LENGTH_INDEFINITE ).show()
1786+ }
1787+ }
1788+
1789+ private fun createIntentToOpenFile (context : Context , file : File ): Intent ? {
1790+ val uri = FileProvider .getUriForFile(context, " ${BuildConfig .APPLICATION_ID } .provider" , file)
1791+ val mime = activity?.contentResolver?.getType(uri) ? : return null
1792+ val intent = Intent (Intent .ACTION_VIEW )
1793+ intent.setDataAndType(uri, mime)
1794+ return intent.addFlags(Intent .FLAG_GRANT_READ_URI_PERMISSION )
1795+ }
1796+
1797+ override fun continueDownload (pendingFileDownload : PendingFileDownload ) {
1798+ Timber .i(" Continuing to download $pendingFileDownload " )
1799+ viewModel.download(pendingFileDownload)
1800+ }
1801+
1802+ override fun cancelDownload () {
1803+ viewModel.closeAndReturnToSourceIfBlankTab()
1804+ }
17901805}
0 commit comments