2 * Copyright © 2016-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.dialogs;
22 import android.annotation.SuppressLint;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.SharedPreferences;
28 import android.graphics.Bitmap;
29 import android.graphics.BitmapFactory;
30 import android.graphics.drawable.BitmapDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.os.Bundle;
33 import android.preference.PreferenceManager;
34 import android.view.KeyEvent;
35 import android.view.View;
36 import android.view.WindowManager;
37 import android.widget.EditText;
39 import androidx.annotation.NonNull;
40 import androidx.fragment.app.DialogFragment; // The AndroidX dialog fragment must be used or an error is produced on API <=22.
42 import com.stoutner.privacybrowser.R;
44 import java.io.ByteArrayOutputStream;
46 public class CreateBookmarkDialog extends DialogFragment {
47 // The public interface is used to send information back to the parent activity.
48 public interface CreateBookmarkListener {
49 void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap);
52 // The create bookmark listener is initialized in `onAttach()` and used in `onCreateDialog()`.
53 private CreateBookmarkListener createBookmarkListener;
55 public void onAttach(@NonNull Context context) {
56 // Run the default commands.
57 super.onAttach(context);
59 // Get a handle for the create bookmark listener from the launching context.
60 createBookmarkListener = (CreateBookmarkListener) context;
63 public static CreateBookmarkDialog createBookmark(String url, String title, Bitmap favoriteIconBitmap) {
64 // Create a favorite icon byte array output stream.
65 ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
67 // Convert the favorite icon to a PNG and place it in the byte array output stream. `0` is for lossless compression (the only option for a PNG).
68 favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
70 // Convert the byte array output stream to a byte array.
71 byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
73 // Create an arguments bundle.
74 Bundle argumentsBundle = new Bundle();
76 // Store the variables in the bundle.
77 argumentsBundle.putString("url", url);
78 argumentsBundle.putString("title", title);
79 argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
81 // Create a new instance of the dialog.
82 CreateBookmarkDialog createBookmarkDialog = new CreateBookmarkDialog();
84 // Add the bundle to the dialog.
85 createBookmarkDialog.setArguments(argumentsBundle);
87 // Return the new dialog.
88 return createBookmarkDialog;
91 // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
92 @SuppressLint("InflateParams")
95 public Dialog onCreateDialog(Bundle savedInstanceState) {
97 Bundle arguments = getArguments();
99 // Remove the incorrect lint warning below that the arguments might be null.
100 assert arguments != null;
102 // Get the strings from the arguments.
103 String url = arguments.getString("url");
104 String title = arguments.getString("title");
106 // Get the favorite icon byte array.
107 byte[] favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array");
109 // Remove the incorrect lint warning below that the favorite icon byte array might be null.
110 assert favoriteIconByteArray != null;
112 // Convert the favorite icon byte array to a bitmap.
113 Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
115 // Get a handle for the shared preferences.
116 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
118 // Get the theme and screenshot preferences.
119 boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
120 boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
122 // Create a drawable version of the favorite icon.
123 Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), favoriteIconBitmap);
125 // Use an alert dialog builder to create the alert dialog.
126 AlertDialog.Builder dialogBuilder;
128 // Set the style according to the theme.
130 dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
132 dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
135 // Set the title and icon.
136 dialogBuilder.setTitle(R.string.create_bookmark);
137 dialogBuilder.setIcon(favoriteIconDrawable);
139 // Remove the warning below that `getLayoutInflater()` might be null.
140 assert getActivity() != null;
142 // Set the view. The parent view is `null` because it will be assigned by the `AlertDialog`.
143 dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.create_bookmark_dialog, null));
145 // Set an `onClick()` listener for the negative button.
146 dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
147 // Do nothing. The `AlertDialog` will close automatically.
150 // Set an `onClick()` listener for the positive button.
151 dialogBuilder.setPositiveButton(R.string.create, (DialogInterface dialog, int which) -> {
152 // Return the `DialogFragment` to the parent activity.
153 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap);
156 // Create an `AlertDialog` from the `AlertDialog.Builder`.
157 AlertDialog alertDialog = dialogBuilder.create();
159 // Remove the warning below that `getWindow()` might be null.
160 assert alertDialog.getWindow() != null;
162 // Disable screenshots if not allowed.
163 if (!allowScreenshots) {
164 alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
167 // Show the keyboard when the `AlertDialog` is displayed on the screen.
168 alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
170 // The alert dialog needs to be shown before `setOnKeyListener()` can be called.
173 // Get a handle for `create_bookmark_name_edittext`.
174 EditText createBookmarkNameEditText = alertDialog.findViewById(R.id.create_bookmark_name_edittext);
176 // Set the current `WebView` title as the text for `create_bookmark_name_edittext`.
177 createBookmarkNameEditText.setText(title);
179 // Allow the `enter` key on the keyboard to create the bookmark from `create_bookmark_name_edittext`.
180 createBookmarkNameEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
181 // If the event is a key-down on the `enter` key, select the `PositiveButton` `Create`.
182 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
183 // Trigger `createBookmarkListener` and return the `DialogFragment` to the parent activity.
184 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap);
186 // Manually dismiss the `AlertDialog`.
187 alertDialog.dismiss();
189 // Consume the event.
191 } else { // If any other key was pressed, do not consume the event.
196 // Set the formattedUrlString as the initial text of `create_bookmark_url_edittext`.
197 EditText createBookmarkUrlEditText = alertDialog.findViewById(R.id.create_bookmark_url_edittext);
198 createBookmarkUrlEditText.setText(url);
200 // Allow the `enter` key on the keyboard to create the bookmark from `create_bookmark_url_edittext`.
201 createBookmarkUrlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
202 // If the event is a key-down on the "enter" key, select the PositiveButton "Create".
203 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
204 // Trigger `createBookmarkListener` and return the DialogFragment to the parent activity.
205 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap);
207 // Manually dismiss the `AlertDialog`.
208 alertDialog.dismiss();
210 // Consume the event.
212 } else { // If any other key was pressed, do not consume the event.
217 // Return the alert dialog.