]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/coroutines/SaveWebpageImageCoroutine.kt
Migrate five classes to Kotlin. https://redmine.stoutner.com/issues/950
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / coroutines / SaveWebpageImageCoroutine.kt
1 /*
2  * Copyright0 2019-2023 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
6  * Privacy Browser Android is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser Android is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.coroutines
21
22 import android.app.Activity
23 import android.graphics.Bitmap
24 import android.graphics.Canvas
25 import android.net.Uri
26 import android.os.Build
27 import android.provider.OpenableColumns
28
29 import com.google.android.material.snackbar.Snackbar
30
31 import com.stoutner.privacybrowser.R
32 import com.stoutner.privacybrowser.views.NestedScrollWebView
33
34 import kotlinx.coroutines.CoroutineScope
35 import kotlinx.coroutines.Dispatchers
36 import kotlinx.coroutines.launch
37 import kotlinx.coroutines.withContext
38
39 import java.io.ByteArrayOutputStream
40
41 class SaveWebpageImageCoroutine {
42     fun save(activity: Activity, fileUri: Uri, nestedScrollWebView: NestedScrollWebView) {
43         // Use a coroutine to save the webpage image.
44         CoroutineScope(Dispatchers.Main).launch {
45             // Create a file name string.
46             val fileNameString: String
47
48             // Query the exact file name if the API >= 26.
49             if (Build.VERSION.SDK_INT >= 26) {
50                 // Get a cursor from the content resolver.
51                 val contentResolverCursor = activity.contentResolver.query(fileUri, null, null, null)
52
53                 // Move to the first row.
54                 contentResolverCursor!!.moveToFirst()
55
56                 // Get the file name from the cursor.
57                 fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
58
59                 // Close the cursor.
60                 contentResolverCursor.close()
61             } else {
62                 // Use the file URI last path segment as the file name string.
63                 fileNameString = fileUri.lastPathSegment!!
64             }
65
66             // Create a saving image snackbar.
67             val savingImageSnackbar = Snackbar.make(nestedScrollWebView, activity.getString(R.string.processing_image, fileNameString), Snackbar.LENGTH_INDEFINITE)
68
69             // Display the saving image snackbar.
70             savingImageSnackbar.show()
71
72             // Create a webpage bitmap.  Once the Minimum API >= 26 Bitmap.Config.RBGA_F16 can be used instead of ARGB_8888.  The nested scroll WebView commands must be run on the UI thread.
73             val webpageBitmap = Bitmap.createBitmap(nestedScrollWebView.getHorizontalScrollRange(), nestedScrollWebView.getVerticalScrollRange(), Bitmap.Config.ARGB_8888)
74
75             // Create a canvas.
76             val webpageCanvas = Canvas(webpageBitmap)
77
78             // Draw the current webpage onto the bitmap.  The nested scroll WebView commands must be run on the UI thread.
79             nestedScrollWebView.draw(webpageCanvas)
80
81             // Compress the image on the IO thread.
82             withContext(Dispatchers.IO) {
83                 // Create a webpage PNG byte array output stream.
84                 val webpageByteArrayOutputStream = ByteArrayOutputStream()
85
86                 // Convert the bitmap to a PNG.  `0` is for lossless compression (the only option for a PNG).  This compression takes a long time.
87                 // Once the minimum API >= 30 this could be replaced with WEBP_LOSSLESS.
88                 webpageBitmap.compress(Bitmap.CompressFormat.PNG, 0, webpageByteArrayOutputStream)
89
90                 try {
91                     // Create an image file output stream.
92                     val imageFileOutputStream = activity.contentResolver.openOutputStream(fileUri)!!
93
94                     // Write the webpage image to the image file.
95                     webpageByteArrayOutputStream.writeTo(imageFileOutputStream)
96
97                     // Close the output stream.
98                     imageFileOutputStream.close()
99
100                     // Update the UI.
101                     withContext(Dispatchers.Main) {
102                         // Dismiss the saving image snackbar.
103                         savingImageSnackbar.dismiss()
104
105                         // Display the image saved snackbar.
106                         Snackbar.make(nestedScrollWebView, activity.getString(R.string.saved, fileNameString), Snackbar.LENGTH_SHORT).show()
107                     }
108                 } catch (exception: Exception) {
109                     // Update the UI.
110                     withContext(Dispatchers.Main) {
111                         // Dismiss the saving image snackbar.
112                         savingImageSnackbar.dismiss()
113
114                         // Display the file saving error.
115                         Snackbar.make(nestedScrollWebView, activity.getString(R.string.error_saving_file, fileNameString, exception), Snackbar.LENGTH_INDEFINITE).show()
116                     }
117                 }
118
119             }
120         }
121     }
122 }