]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/coroutines/SaveUrlCoroutine.kt
Bump the minimum API to 26. https://redmine.stoutner.com/issues/1163
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / coroutines / SaveUrlCoroutine.kt
index d2765814b7b210291addee03dd37a5ba5fbce54e..f8adc5b0f187862fbd611cab2d373becaad2645e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2020-2024 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
@@ -22,7 +22,6 @@ package com.stoutner.privacybrowser.coroutines
 import android.app.Activity
 import android.content.Context
 import android.net.Uri
-import android.os.Build
 import android.provider.OpenableColumns
 import android.util.Base64
 import android.webkit.CookieManager
@@ -44,37 +43,33 @@ import java.io.InputStream
 import java.net.HttpURLConnection
 import java.net.URL
 import java.text.NumberFormat
+import java.util.Date
 
 class SaveUrlCoroutine {
     fun save(context: Context, activity: Activity, urlString: String, fileUri: Uri, userAgent: String, cookiesEnabled: Boolean) {
+        // Create a canceled boolean.
+        var canceled = false
+
         // Use a coroutine to save the URL.
         CoroutineScope(Dispatchers.Main).launch {
-            // Create a file name string.
-            val fileNameString: String
-
-            // Query the exact file name if the API >= 26.
-            if (Build.VERSION.SDK_INT >= 26) {
-                // Get a cursor from the content resolver.
-                val contentResolverCursor = activity.contentResolver.query(fileUri, null, null, null)!!
+            // Get a cursor from the content resolver.
+            val contentResolverCursor = activity.contentResolver.query(fileUri, null, null, null)!!
 
-                // Move to the first row.
-                contentResolverCursor.moveToFirst()
+            // Move to the first row.
+            contentResolverCursor.moveToFirst()
 
-                // Get the file name from the cursor.
-                fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
+            // Get the file name from the cursor.
+            val fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
 
-                // Close the cursor.
-                contentResolverCursor.close()
-            } else {
-                // Use the file URI last path segment as the file name string.
-                fileNameString = fileUri.lastPathSegment!!
-            }
+            // Close the cursor.
+            contentResolverCursor.close()
 
             // Get a handle for the no swipe view pager.
             val webViewViewPager2 = activity.findViewById<ViewPager2>(R.id.webview_viewpager2)
 
             // Create a saving file snackbar.
             val savingFileSnackbar = Snackbar.make(webViewViewPager2, activity.getString(R.string.saving_file, 0, fileNameString), Snackbar.LENGTH_INDEFINITE)
+                                             .setAction(R.string.cancel) { canceled = true }
 
             // Display the saving file snackbar.
             savingFileSnackbar.show()
@@ -150,40 +145,52 @@ class SaveUrlCoroutine {
                             val inputStream: InputStream = BufferedInputStream(httpUrlConnection.inputStream)
 
                             // Initialize the conversion buffer byte array.
-                            // This is set to a megabyte so that frequent updating of the snackbar doesn't freeze the interface on download.  <https://redmine.stoutner.com/issues/709>
-                            val conversionBufferByteArray = ByteArray(1048576)
+                            // This is set to a 50,000 bytes so that frequent updating of the snackbar doesn't freeze the interface on download, although `inputStream.read` currently uses 8,000 as an upper limit.
+                            // <https://redmine.stoutner.com/issues/709>
+                            val conversionBufferByteArray = ByteArray(50_000)
 
-                            // Initialize the downloaded kilobytes counter.
-                            var downloadedKilobytesCounter: Long = 0
+                            // Initialize the longs.
+                            var downloadedBytesCounterLong: Long = 0
+                            var lastSnackbarUpdateLong: Long = 0
 
                             // Define the buffer length variable.
                             var bufferLength: Int
 
                             // Attempt to read data from the input stream and store it in the output stream.  Also store the amount of data read in the buffer length variable.
-                            while (inputStream.read(conversionBufferByteArray).also { bufferLength = it } > 0) {  // Proceed while the amount of data stored in the buffer in > 0.
+                            while ((inputStream.read(conversionBufferByteArray).also { bufferLength = it } > 0) && !canceled) {  // Proceed while the amount of data stored in the buffer in > 0.
                                 // Write the contents of the conversion buffer to the file output stream.
                                 outputStream.write(conversionBufferByteArray, 0, bufferLength)
 
-                                // Update the downloaded kilobytes counter.
-                                downloadedKilobytesCounter += bufferLength
+                                // Update the downloaded bytes counter.
+                                downloadedBytesCounterLong += bufferLength
 
                                 // Format the number of bytes downloaded.
-                                val formattedNumberOfBytesDownloadedString = NumberFormat.getInstance().format(downloadedKilobytesCounter)
-
-                                // Update the UI.
-                                withContext(Dispatchers.Main) {
-                                    // Check to see if the file size is known.
-                                    if (fileSize == -1L) {  // The size of the download file is not known.
-                                        // Update the snackbar.
-                                        savingFileSnackbar.setText(activity.getString(R.string.saving_file_progress, formattedNumberOfBytesDownloadedString, fileNameString))
-                                    } else {  // The size of the download file is known.
-                                        // Calculate the download percentage.
-                                        val downloadPercentage = downloadedKilobytesCounter * 100 / fileSize
-
-                                        // Update the snackbar.
-                                        savingFileSnackbar.setText(activity.getString(R.string.saving_file_percentage_progress, downloadPercentage, formattedNumberOfBytesDownloadedString, formattedFileSize,
-                                            fileNameString)
-                                        )
+                                val formattedNumberOfBytesDownloadedString = NumberFormat.getInstance().format(downloadedBytesCounterLong)
+
+                                // Get the current time.
+                                val currentTimeLong = Date().time
+
+                                // Update the snackbar if more than 100 milliseconds have passed since the last update.
+                                // Updating the snackbar is so resource intensive that it will throttle the download if it is done too frequently.
+                                if (currentTimeLong - lastSnackbarUpdateLong > 100) {
+                                    // Store the update time.
+                                    lastSnackbarUpdateLong = currentTimeLong
+
+                                    // Update the UI.
+                                    withContext(Dispatchers.Main) {
+                                        // Check to see if the file size is known.
+                                        if (fileSize == -1L) {  // The size of the download file is not known.
+                                            // Update the snackbar.
+                                            savingFileSnackbar.setText(activity.getString(R.string.saving_file_progress, formattedNumberOfBytesDownloadedString, fileNameString))
+                                        } else {  // The size of the download file is known.
+                                            // Calculate the download percentage.
+                                            val downloadPercentage = downloadedBytesCounterLong * 100 / fileSize
+
+                                            // Update the snackbar.
+                                            savingFileSnackbar.setText(activity.getString(R.string.saving_file_percentage_progress, downloadPercentage, formattedNumberOfBytesDownloadedString, formattedFileSize,
+                                                fileNameString)
+                                            )
+                                        }
                                     }
                                 }
                             }
@@ -204,8 +211,13 @@ class SaveUrlCoroutine {
                         // Dismiss the saving file snackbar.
                         savingFileSnackbar.dismiss()
 
-                        // Display the file saved snackbar.
-                        Snackbar.make(webViewViewPager2, activity.getString(R.string.saved, fileNameString), Snackbar.LENGTH_LONG).show()
+                        // Display a final disposition snackbar.
+                        if (canceled)
+                            // Display the download cancelled snackbar.
+                            Snackbar.make(webViewViewPager2, activity.getString(R.string.download_cancelled), Snackbar.LENGTH_SHORT).show()
+                        else
+                            // Display the file saved snackbar.
+                            Snackbar.make(webViewViewPager2, activity.getString(R.string.saved, fileNameString), Snackbar.LENGTH_LONG).show()
                     }
                 } catch (exception: Exception) {
                     // Update the UI.
@@ -220,4 +232,4 @@ class SaveUrlCoroutine {
             }
         }
     }
-}
\ No newline at end of file
+}