X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fcoroutines%2FSaveWebpageImageCoroutine.kt;fp=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fcoroutines%2FSaveWebpageImageCoroutine.kt;h=1c3a2c0310104f0f1d876f9aa22974bf8bb1fae9;hb=5186b668274b09e37b371c0a134e53255c98ad98;hp=0000000000000000000000000000000000000000;hpb=514e93baaa8389dc9c5abdb79e68c890c260b8d3;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/coroutines/SaveWebpageImageCoroutine.kt b/app/src/main/java/com/stoutner/privacybrowser/coroutines/SaveWebpageImageCoroutine.kt new file mode 100644 index 00000000..1c3a2c03 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/coroutines/SaveWebpageImageCoroutine.kt @@ -0,0 +1,122 @@ +/* + * Copyright0 2019-2023 Soren Stoutner . + * + * This file is part of Privacy Browser Android . + * + * Privacy Browser Android is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser Android is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser Android. If not, see . + */ + +package com.stoutner.privacybrowser.coroutines + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Canvas +import android.net.Uri +import android.os.Build +import android.provider.OpenableColumns + +import com.google.android.material.snackbar.Snackbar + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.views.NestedScrollWebView + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +import java.io.ByteArrayOutputStream + +class SaveWebpageImageCoroutine { + fun save(activity: Activity, fileUri: Uri, nestedScrollWebView: NestedScrollWebView) { + // Use a coroutine to save the webpage image. + 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) + + // Move to the first row. + contentResolverCursor!!.moveToFirst() + + // Get the file name from the cursor. + 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!! + } + + // Create a saving image snackbar. + val savingImageSnackbar = Snackbar.make(nestedScrollWebView, activity.getString(R.string.processing_image, fileNameString), Snackbar.LENGTH_INDEFINITE) + + // Display the saving image snackbar. + savingImageSnackbar.show() + + // 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. + val webpageBitmap = Bitmap.createBitmap(nestedScrollWebView.getHorizontalScrollRange(), nestedScrollWebView.getVerticalScrollRange(), Bitmap.Config.ARGB_8888) + + // Create a canvas. + val webpageCanvas = Canvas(webpageBitmap) + + // Draw the current webpage onto the bitmap. The nested scroll WebView commands must be run on the UI thread. + nestedScrollWebView.draw(webpageCanvas) + + // Compress the image on the IO thread. + withContext(Dispatchers.IO) { + // Create a webpage PNG byte array output stream. + val webpageByteArrayOutputStream = ByteArrayOutputStream() + + // Convert the bitmap to a PNG. `0` is for lossless compression (the only option for a PNG). This compression takes a long time. + // Once the minimum API >= 30 this could be replaced with WEBP_LOSSLESS. + webpageBitmap.compress(Bitmap.CompressFormat.PNG, 0, webpageByteArrayOutputStream) + + try { + // Create an image file output stream. + val imageFileOutputStream = activity.contentResolver.openOutputStream(fileUri)!! + + // Write the webpage image to the image file. + webpageByteArrayOutputStream.writeTo(imageFileOutputStream) + + // Close the output stream. + imageFileOutputStream.close() + + // Update the UI. + withContext(Dispatchers.Main) { + // Dismiss the saving image snackbar. + savingImageSnackbar.dismiss() + + // Display the image saved snackbar. + Snackbar.make(nestedScrollWebView, activity.getString(R.string.saved, fileNameString), Snackbar.LENGTH_SHORT).show() + } + } catch (exception: Exception) { + // Update the UI. + withContext(Dispatchers.Main) { + // Dismiss the saving image snackbar. + savingImageSnackbar.dismiss() + + // Display the file saving error. + Snackbar.make(nestedScrollWebView, activity.getString(R.string.error_saving_file, fileNameString, exception), Snackbar.LENGTH_INDEFINITE).show() + } + } + + } + } + } +} \ No newline at end of file