2 * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
6 * Privacy Browser 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.
11 * Privacy Browser 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.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.asynctasks;
22 import android.app.Activity;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.net.Uri;
26 import android.os.AsyncTask;
28 import com.google.android.material.snackbar.Snackbar;
30 import com.stoutner.privacybrowser.R;
31 import com.stoutner.privacybrowser.views.NestedScrollWebView;
33 import java.io.ByteArrayOutputStream;
34 import java.io.OutputStream;
35 import java.lang.ref.WeakReference;
37 public class SaveWebpageImage extends AsyncTask<Void, Void, String> {
38 // Declare the weak references.
39 private final WeakReference<Activity> activityWeakReference;
40 private final WeakReference<NestedScrollWebView> nestedScrollWebViewWeakReference;
42 // Declare the class constants.
43 private final String SUCCESS = "Success";
45 // Declare the class variables.
46 private Snackbar savingImageSnackbar;
47 private Bitmap webpageBitmap;
48 private final String filePathString;
50 // The public constructor.
51 public SaveWebpageImage(Activity activity, String filePathString, NestedScrollWebView nestedScrollWebView) {
52 // Populate the weak references.
53 activityWeakReference = new WeakReference<>(activity);
54 nestedScrollWebViewWeakReference = new WeakReference<>(nestedScrollWebView);
56 // Populate the class variables.
57 this.filePathString = filePathString;
60 // `onPreExecute()` operates on the UI thread.
62 protected void onPreExecute() {
63 // Get handles for the activity and the nested scroll WebView.
64 Activity activity = activityWeakReference.get();
65 NestedScrollWebView nestedScrollWebView = nestedScrollWebViewWeakReference.get();
67 // Abort if the activity or the nested scroll WebView is gone.
68 if ((activity == null) || activity.isFinishing() || nestedScrollWebView == null) {
72 // Create a saving image snackbar.
73 savingImageSnackbar = Snackbar.make(nestedScrollWebView, activity.getString(R.string.processing_image) + " " + filePathString, Snackbar.LENGTH_INDEFINITE);
75 // Display the saving image snackbar.
76 savingImageSnackbar.show();
78 // 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.
79 webpageBitmap = Bitmap.createBitmap(nestedScrollWebView.getHorizontalScrollRange(), nestedScrollWebView.getVerticalScrollRange(), Bitmap.Config.ARGB_8888);
82 Canvas webpageCanvas = new Canvas(webpageBitmap);
84 // Draw the current webpage onto the bitmap. The nested scroll WebView commands must be run on the UI thread.
85 nestedScrollWebView.draw(webpageCanvas);
89 protected String doInBackground(Void... Void) {
90 // Get a handle for the activity.
91 Activity activity = activityWeakReference.get();
93 // Abort if the activity is gone.
94 if ((activity == null) || activity.isFinishing()) {
98 // Create a webpage PNG byte array output stream.
99 ByteArrayOutputStream webpageByteArrayOutputStream = new ByteArrayOutputStream();
101 // 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.
102 webpageBitmap.compress(Bitmap.CompressFormat.PNG, 0, webpageByteArrayOutputStream);
104 // Create a file creation disposition string.
105 String fileCreationDisposition = SUCCESS;
108 // Create an image file output stream.
109 OutputStream imageFileOutputStream = activity.getContentResolver().openOutputStream(Uri.parse(filePathString));
111 // Write the webpage image to the image file.
112 webpageByteArrayOutputStream.writeTo(imageFileOutputStream);
113 } catch (Exception exception) {
114 // Store the error in the file creation disposition string.
115 fileCreationDisposition = exception.toString();
118 // Return the file creation disposition string.
119 return fileCreationDisposition;
122 // `onPostExecute()` operates on the UI thread.
124 protected void onPostExecute(String fileCreationDisposition) {
125 // Get handles for the weak references.
126 Activity activity = activityWeakReference.get();
127 NestedScrollWebView nestedScrollWebView = nestedScrollWebViewWeakReference.get();
129 // Abort if the activity is gone.
130 if ((activity == null) || activity.isFinishing()) {
134 // Dismiss the saving image snackbar.
135 savingImageSnackbar.dismiss();
137 // Display a file creation disposition snackbar.
138 if (fileCreationDisposition.equals(SUCCESS)) {
139 // Display the file saved snackbar.
140 Snackbar.make(nestedScrollWebView, activity.getString(R.string.file_saved) + " " + filePathString, Snackbar.LENGTH_SHORT).show();
142 // Display the file saving error.
143 Snackbar.make(nestedScrollWebView, activity.getString(R.string.error_saving_file) + " " + fileCreationDisposition, Snackbar.LENGTH_INDEFINITE).show();