2 * Copyright © 2019 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.os.AsyncTask;
27 import com.google.android.material.snackbar.Snackbar;
29 import com.stoutner.privacybrowser.R;
30 import com.stoutner.privacybrowser.views.NestedScrollWebView;
31 import com.stoutner.privacybrowser.views.NoSwipeViewPager;
33 import java.io.ByteArrayOutputStream;
35 import java.io.FileOutputStream;
36 import java.lang.ref.WeakReference;
38 public class SaveWebpageImage extends AsyncTask<String, Void, String> {
39 // Define the weak references.
40 private WeakReference<Activity> activityWeakReference;
41 private WeakReference<NestedScrollWebView> nestedScrollWebViewWeakReference;
43 // Define a success string constant.
44 private final String SUCCESS = "Success";
46 // Define the saving image snackbar and the webpage bitmap.
47 private Snackbar savingImageSnackbar;
48 private Bitmap webpageBitmap;
50 // The public constructor.
51 public SaveWebpageImage(Activity activity, NestedScrollWebView nestedScrollWebView) {
52 // Populate the weak references.
53 activityWeakReference = new WeakReference<>(activity);
54 nestedScrollWebViewWeakReference = new WeakReference<>(nestedScrollWebView);
57 // `onPreExecute()` operates on the UI thread.
59 protected void onPreExecute() {
60 // Get a handle for the activity and the nested scroll WebView.
61 Activity activity = activityWeakReference.get();
62 NestedScrollWebView nestedScrollWebView = nestedScrollWebViewWeakReference.get();
64 // Abort if the activity or the nested scroll WebView is gone.
65 if ((activity == null) || activity.isFinishing() || nestedScrollWebView == null) {
69 // Create a saving image snackbar.
70 savingImageSnackbar = Snackbar.make(nestedScrollWebView, R.string.saving_image, Snackbar.LENGTH_INDEFINITE);
72 // Display the saving image snackbar.
73 savingImageSnackbar.show();
75 // 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.
76 webpageBitmap = Bitmap.createBitmap(nestedScrollWebView.getHorizontalScrollRange(), nestedScrollWebView.getVerticalScrollRange(), Bitmap.Config.ARGB_8888);
79 Canvas webpageCanvas = new Canvas(webpageBitmap);
81 // Draw the current webpage onto the bitmap. The nested scroll WebView commands must be run on the UI thread.
82 nestedScrollWebView.draw(webpageCanvas);
86 protected String doInBackground(String... fileName) {
87 // Get a handle for the activity.
88 Activity activity = activityWeakReference.get();
90 // Abort if the activity is gone.
91 if ((activity == null) || activity.isFinishing()) {
95 // Create a webpage PNG byte array output stream.
96 ByteArrayOutputStream webpageByteArrayOutputStream = new ByteArrayOutputStream();
98 // Convert the bitmap to a PNG. `0` is for lossless compression (the only option for a PNG). This compression takes a long time.
99 webpageBitmap.compress(Bitmap.CompressFormat.PNG, 0, webpageByteArrayOutputStream);
101 // Get a file for the image.
102 File imageFile = new File(fileName[0]);
104 // Delete the current file if it exists.
105 if (imageFile.exists()) {
106 //noinspection ResultOfMethodCallIgnored
110 // Create a file creation disposition string.
111 String fileCreationDisposition = SUCCESS;
114 // Create an image file output stream.
115 FileOutputStream imageFileOutputStream = new FileOutputStream(imageFile);
117 // Write the webpage image to the image file.
118 webpageByteArrayOutputStream.writeTo(imageFileOutputStream);
119 } catch (Exception exception) {
120 // Store the error in the file creation disposition string.
121 fileCreationDisposition = exception.toString();
124 // Return the file creation disposition string.
125 return fileCreationDisposition;
128 // `onPostExecute()` operates on the UI thread.
130 protected void onPostExecute(String fileCreationDisposition) {
131 // Get a handle for the activity.
132 Activity activity = activityWeakReference.get();
134 // Abort if the activity is gone.
135 if ((activity == null) || activity.isFinishing()) {
139 // Get a handle for the no swipe view pager.
140 NoSwipeViewPager noSwipeViewPager = activity.findViewById(R.id.webviewpager);
142 // Dismiss the saving image snackbar.
143 savingImageSnackbar.dismiss();
145 // Display a file creation disposition snackbar.
146 if (fileCreationDisposition.equals(SUCCESS)) {
147 Snackbar.make(noSwipeViewPager, R.string.image_saved, Snackbar.LENGTH_SHORT).show();
149 Snackbar.make(noSwipeViewPager, activity.getString(R.string.error_saving_image) + " " + fileCreationDisposition, Snackbar.LENGTH_INDEFINITE).show();