Release 3.9.
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / asynctasks / SaveAboutVersionImage.java
1 /*
2  * Copyright © 2020-2021 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacybrowser.asynctasks;
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.AsyncTask;
27 import android.widget.LinearLayout;
28
29 import com.google.android.material.snackbar.Snackbar;
30
31 import com.stoutner.privacybrowser.R;
32
33 import java.io.ByteArrayOutputStream;
34 import java.io.OutputStream;
35 import java.lang.ref.WeakReference;
36
37 public class SaveAboutVersionImage extends AsyncTask<Void, Void, String> {
38     // Declare the weak references.
39     private final WeakReference<Activity> activityWeakReference;
40     private final WeakReference<LinearLayout> aboutVersionLinearLayoutWeakReference;
41
42     // Declare the class constants.
43     private final String SUCCESS = "Success";
44
45     // Declare the class variables.
46     private Snackbar savingImageSnackbar;
47     private Bitmap aboutVersionBitmap;
48     private final String fileNameString;
49
50     // The public constructor.
51     public SaveAboutVersionImage(Activity activity, String fileNameString, LinearLayout aboutVersionLinearLayout) {
52         // Populate the weak references.
53         activityWeakReference = new WeakReference<>(activity);
54         aboutVersionLinearLayoutWeakReference = new WeakReference<>(aboutVersionLinearLayout);
55
56         // Store the class variables.
57         this.fileNameString = fileNameString;
58     }
59
60     // `onPreExecute()` operates on the UI thread.
61     @Override
62     protected void onPreExecute() {
63         // Get handles for the activity and the linear layout.
64         Activity activity = activityWeakReference.get();
65         LinearLayout aboutVersionLinearLayout = aboutVersionLinearLayoutWeakReference.get();
66
67         // Abort if the activity or the linear layout is gone.
68         if ((activity == null) || activity.isFinishing() || aboutVersionLinearLayout == null) {
69             return;
70         }
71
72         // Create a saving image snackbar.
73         savingImageSnackbar = Snackbar.make(aboutVersionLinearLayout, activity.getString(R.string.processing_image) + "  " + fileNameString, Snackbar.LENGTH_INDEFINITE);
74
75         // Display the saving image snackbar.
76         savingImageSnackbar.show();
77
78         // Create the about version bitmap.  This can be replaced by PixelCopy once the minimum API >= 26.
79         // Once the Minimum API >= 26 Bitmap.Config.RBGA_F16 can be used instead of ARGB_8888.  The linear layout commands must be run on the UI thread.
80         aboutVersionBitmap = Bitmap.createBitmap(aboutVersionLinearLayout.getWidth(), aboutVersionLinearLayout.getHeight(), Bitmap.Config.ARGB_8888);
81
82         // Create a canvas.
83         Canvas aboutVersionCanvas = new Canvas(aboutVersionBitmap);
84
85         // Draw the current about version onto the bitmap.  The linear layout commands must be run on the UI thread.
86         aboutVersionLinearLayout.draw(aboutVersionCanvas);
87     }
88
89     @Override
90     protected String doInBackground(Void... Void) {
91         // Get a handle for the activity.
92         Activity activity = activityWeakReference.get();
93
94         // Abort if the activity is gone.
95         if (((activity == null) || activity.isFinishing())) {
96             return "";
97         }
98
99         // Create an about version PNG byte array output stream.
100         ByteArrayOutputStream aboutVersionByteArrayOutputStream = new ByteArrayOutputStream();
101
102         // 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.
103         aboutVersionBitmap.compress(Bitmap.CompressFormat.PNG, 0, aboutVersionByteArrayOutputStream);
104
105         // Create a file creation disposition string.
106         String fileCreationDisposition = SUCCESS;
107
108         try {
109             // Open an output stream.
110             OutputStream outputStream = activity.getContentResolver().openOutputStream(Uri.parse(fileNameString));
111
112             // Write the webpage image to the image file.
113             aboutVersionByteArrayOutputStream.writeTo(outputStream);
114
115             // Close the output stream.
116             outputStream.close();
117         } catch (Exception exception) {
118             // Store the error in the file creation disposition string.
119             fileCreationDisposition = exception.toString();
120         }
121
122         // return the file creation disposition string.
123         return fileCreationDisposition;
124     }
125
126     // `onPostExecute()` operates on the UI thread.
127     @Override
128     protected void onPostExecute(String fileCreationDisposition) {
129         // Get handles for the weak references.
130         Activity activity = activityWeakReference.get();
131         LinearLayout aboutVersionLinearLayout = aboutVersionLinearLayoutWeakReference.get();
132
133         // Abort if the activity is gone.
134         if ((activity == null) || activity.isFinishing()) {
135             return;
136         }
137
138         // Dismiss the saving image snackbar.
139         savingImageSnackbar.dismiss();
140
141         // Display a file creation disposition snackbar.
142         if (fileCreationDisposition.equals(SUCCESS)) {
143             // Create a file saved snackbar.
144             Snackbar.make(aboutVersionLinearLayout, activity.getString(R.string.file_saved) + "  " + fileNameString, Snackbar.LENGTH_SHORT).show();
145         } else {
146             Snackbar.make(aboutVersionLinearLayout, activity.getString(R.string.error_saving_file) + "  " + fileCreationDisposition, Snackbar.LENGTH_INDEFINITE).show();
147         }
148     }
149 }