@@ -60,6 +60,7 @@ import com.duckduckgo.app.browser.BrowserTabViewModel.*
6060import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter
6161import com.duckduckgo.app.browser.downloader.FileDownloadNotificationManager
6262import com.duckduckgo.app.browser.downloader.FileDownloader
63+ import com.duckduckgo.app.browser.downloader.FileDownloader.FileDownloadListener
6364import com.duckduckgo.app.browser.downloader.FileDownloader.PendingFileDownload
6465import com.duckduckgo.app.browser.filechooser.FileChooserIntentBuilder
6566import com.duckduckgo.app.browser.model.BasicAuthenticationCredentials
@@ -244,6 +245,14 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
244245 override fun onCreate (savedInstanceState : Bundle ? ) {
245246 super .onCreate(savedInstanceState)
246247 renderer = BrowserTabFragmentRenderer ()
248+ if (savedInstanceState != null ) {
249+ updateFragmentListener()
250+ }
251+ }
252+
253+ private fun updateFragmentListener () {
254+ val fragment = fragmentManager?.findFragmentByTag(DOWNLOAD_CONFIRMATION_TAG ) as ? DownloadConfirmationFragment
255+ fragment?.downloadListener = createDownloadListener()
247256 }
248257
249258 override fun onCreateView (inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ): View ? {
@@ -335,9 +344,15 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
335344 override fun onPause () {
336345 daxDialog = null
337346 logoHidingListener.onPause()
347+ dismissDownloadFragment()
338348 super .onPause()
339349 }
340350
351+ private fun dismissDownloadFragment () {
352+ val fragment = fragmentManager?.findFragmentByTag(DOWNLOAD_CONFIRMATION_TAG ) as ? DownloadConfirmationFragment
353+ fragment?.dismiss()
354+ }
355+
341356 private fun createPopupMenu () {
342357 popupMenu = BrowserPopupMenu (layoutInflater)
343358 val view = popupMenu.contentView
@@ -509,7 +524,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
509524 )
510525 )
511526 }
512- is Command .DownloadImage -> requestImageDownload(it.url)
527+ is Command .DownloadImage -> requestImageDownload(it.url, it.requestUserConfirmation )
513528 is Command .FindInPageCommand -> webView?.findAllAsync(it.searchTerm)
514529 is Command .DismissFindInPage -> webView?.findAllAsync(" " )
515530 is Command .ShareLink -> launchSharePageChooser(it.url)
@@ -735,6 +750,10 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
735750 omnibarTextInput.onFocusChangeListener =
736751 OnFocusChangeListener { _, hasFocus: Boolean ->
737752 viewModel.onOmnibarInputStateChanged(omnibarTextInput.text.toString(), hasFocus, false )
753+ if (! hasFocus) {
754+ omnibarTextInput.hideKeyboard()
755+ focusDummy.requestFocus()
756+ }
738757 }
739758
740759 omnibarTextInput.onBackKeyListener = object : KeyboardAwareEditText .OnBackKeyListener {
@@ -809,7 +828,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
809828 }
810829
811830 it.setDownloadListener { url, _, contentDisposition, mimeType, _ ->
812- requestFileDownload(url, contentDisposition, mimeType)
831+ requestFileDownload(url, contentDisposition, mimeType, true )
813832 }
814833
815834 it.setOnTouchListener { _, _ ->
@@ -1011,7 +1030,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
10111030 webView = null
10121031 }
10131032
1014- private fun requestFileDownload (url : String , contentDisposition : String , mimeType : String ) {
1033+ private fun requestFileDownload (url : String , contentDisposition : String , mimeType : String , requestUserConfirmation : Boolean ) {
10151034 pendingFileDownload = PendingFileDownload (
10161035 url = url,
10171036 contentDisposition = contentDisposition,
@@ -1020,48 +1039,72 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
10201039 subfolder = Environment .DIRECTORY_DOWNLOADS
10211040 )
10221041
1023- downloadFileWithPermissionCheck()
1042+ if (hasWriteStoragePermission()) {
1043+ downloadFile(requestUserConfirmation)
1044+ } else {
1045+ requestWriteStoragePermission()
1046+ }
10241047 }
10251048
1026- private fun requestImageDownload (url : String ) {
1049+ private fun requestImageDownload (url : String , requestUserConfirmation : Boolean ) {
10271050 pendingFileDownload = PendingFileDownload (
10281051 url = url,
10291052 userAgent = userAgentProvider.getUserAgent(),
10301053 subfolder = Environment .DIRECTORY_PICTURES
10311054 )
10321055
1033- downloadFileWithPermissionCheck()
1034- }
1035-
1036- private fun downloadFileWithPermissionCheck () {
10371056 if (hasWriteStoragePermission()) {
1038- downloadFile()
1057+ downloadFile(requestUserConfirmation )
10391058 } else {
10401059 requestWriteStoragePermission()
10411060 }
10421061 }
10431062
10441063 @AnyThread
1045- private fun downloadFile () {
1064+ private fun downloadFile (requestUserConfirmation : Boolean ) {
10461065 val pendingDownload = pendingFileDownload
10471066 pendingFileDownload = null
1048- thread {
1049- fileDownloader.download(pendingDownload, object : FileDownloader .FileDownloadListener {
1050- override fun downloadStarted () {
1051- fileDownloadNotificationManager.showDownloadInProgressNotification()
1052- }
10531067
1054- override fun downloadFinished (file : File , mimeType : String? ) {
1055- MediaScannerConnection .scanFile(context, arrayOf(file.absolutePath), null ) { _, uri ->
1056- fileDownloadNotificationManager.showDownloadFinishedNotification(file.name, uri, mimeType)
1057- }
1058- }
1068+ if (pendingDownload == null ) {
1069+ return
1070+ }
1071+
1072+ val downloadListener = createDownloadListener()
1073+ if (requestUserConfirmation) {
1074+ requestDownloadConfirmation(pendingDownload, downloadListener)
1075+ } else {
1076+ completeDownload(pendingDownload, downloadListener)
1077+ }
1078+ }
10591079
1060- override fun downloadFailed (message : String ) {
1061- Timber .w(" Failed to download file [$message ]" )
1062- fileDownloadNotificationManager.showDownloadFailedNotification()
1080+ private fun createDownloadListener (): FileDownloadListener {
1081+ return object : FileDownloadListener {
1082+ override fun downloadStarted () {
1083+ fileDownloadNotificationManager.showDownloadInProgressNotification()
1084+ }
1085+
1086+ override fun downloadFinished (file : File , mimeType : String? ) {
1087+ MediaScannerConnection .scanFile(context, arrayOf(file.absolutePath), null ) { _, uri ->
1088+ fileDownloadNotificationManager.showDownloadFinishedNotification(file.name, uri, mimeType)
10631089 }
1064- })
1090+ }
1091+
1092+ override fun downloadFailed (message : String ) {
1093+ Timber .w(" Failed to download file [$message ]" )
1094+ fileDownloadNotificationManager.showDownloadFailedNotification()
1095+ }
1096+ }
1097+ }
1098+
1099+ private fun requestDownloadConfirmation (pendingDownload : PendingFileDownload , downloadListener : FileDownloadListener ) {
1100+ fragmentManager?.let {
1101+ DownloadConfirmationFragment .instance(pendingDownload, downloadListener).show(it, DOWNLOAD_CONFIRMATION_TAG )
1102+ }
1103+ }
1104+
1105+ private fun completeDownload (pendingDownload : PendingFileDownload , callback : FileDownloadListener ) {
1106+ thread {
1107+ fileDownloader.download(pendingDownload, callback)
10651108 }
10661109 }
10671110
@@ -1084,7 +1127,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
10841127 if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE ) {
10851128 if ((grantResults.isNotEmpty()) && grantResults[0 ] == PackageManager .PERMISSION_GRANTED ) {
10861129 Timber .i(" Write external storage permission granted" )
1087- downloadFile()
1130+ downloadFile(requestUserConfirmation = false )
10881131 } else {
10891132 Timber .i(" Write external storage permission refused" )
10901133 Snackbar .make(toolbar, R .string.permissionRequiredToDownload, Snackbar .LENGTH_LONG ).show()
@@ -1126,6 +1169,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope {
11261169 private const val URL_BUNDLE_KEY = " url"
11271170
11281171 private const val AUTHENTICATION_DIALOG_TAG = " AUTH_DIALOG_TAG"
1172+ private const val DOWNLOAD_CONFIRMATION_TAG = " DOWNLOAD_CONFIRMATION_TAG"
11291173 private const val DAX_DIALOG_DIALOG_TAG = " DAX_DIALOG_TAG"
11301174
11311175 private const val MAX_PROGRESS = 100
0 commit comments