2 * Copyright © 2016-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android 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 Android 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 Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.dialogs
22 import android.app.Dialog
23 import android.content.Context
24 import android.content.DialogInterface
25 import android.graphics.Bitmap
26 import android.graphics.BitmapFactory
27 import android.graphics.drawable.BitmapDrawable
28 import android.graphics.drawable.Drawable
29 import android.os.Bundle
30 import android.view.KeyEvent
31 import android.view.View
32 import android.view.WindowManager
33 import android.widget.EditText
35 import androidx.appcompat.app.AlertDialog
36 import androidx.fragment.app.DialogFragment
37 import androidx.preference.PreferenceManager
39 import com.stoutner.privacybrowser.R
41 import java.io.ByteArrayOutputStream
43 // Define the class constants.
44 private const val URL_STRING = "url_string"
45 private const val TITLE = "title"
46 private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
48 class CreateBookmarkDialog : DialogFragment() {
50 fun createBookmark(urlString: String, title: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog {
51 // Create a favorite icon byte array output stream.
52 val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
54 // 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).
55 favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
57 // Convert the byte array output stream to a byte array.
58 val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
60 // Create an arguments bundle.
61 val argumentsBundle = Bundle()
63 // Store the variables in the bundle.
64 argumentsBundle.putString(URL_STRING, urlString)
65 argumentsBundle.putString(TITLE, title)
66 argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
68 // Create a new instance of the dialog.
69 val createBookmarkDialog = CreateBookmarkDialog()
71 // Add the bundle to the dialog.
72 createBookmarkDialog.arguments = argumentsBundle
74 // Return the new dialog.
75 return createBookmarkDialog
79 // Declare the class variables
80 private lateinit var createBookmarkListener: CreateBookmarkListener
82 // The public interface is used to send information back to the parent activity.
83 interface CreateBookmarkListener {
84 fun createBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
87 override fun onAttach(context: Context) {
88 // Run the default commands.
89 super.onAttach(context)
91 // Get a handle for the create bookmark listener from the launching context.
92 createBookmarkListener = context as CreateBookmarkListener
95 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
97 val arguments = requireArguments()
99 // Get the contents of the arguments.
100 val urlString = arguments.getString(URL_STRING)
101 val title = arguments.getString(TITLE)
102 val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
104 // Convert the favorite icon byte array to a bitmap.
105 val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
107 // Use an alert dialog builder to create the dialog.
108 val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
111 dialogBuilder.setTitle(R.string.create_bookmark)
113 // Create a drawable version of the favorite icon.
114 val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
117 dialogBuilder.setIcon(favoriteIconDrawable)
120 dialogBuilder.setView(R.layout.create_bookmark_dialog)
122 // Set a listener on the cancel button. Using `null` as the listener closes the dialog without doing anything else.
123 dialogBuilder.setNegativeButton(R.string.cancel, null)
125 // Set a listener on the create button.
126 dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
127 // Return the dialog fragment and the favorite icon bitmap to the parent activity.
128 createBookmarkListener.createBookmark(this, favoriteIconBitmap)
131 // Create an alert dialog from the builder.
132 val alertDialog = dialogBuilder.create()
135 // Get a handle for the shared preferences.
136 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
138 // Get the screenshot preference.
139 val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
141 // Disable screenshots if not allowed.
142 if (!allowScreenshots) {
143 alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
146 // The alert dialog needs to be shown before the contents can be modified.
149 // Get a handle for the edit texts.
150 val createBookmarkNameEditText = alertDialog.findViewById<EditText>(R.id.create_bookmark_name_edittext)!!
151 val createBookmarkUrlEditText = alertDialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)!!
153 // Set the initial texts for the edit texts.
154 createBookmarkNameEditText.setText(title)
155 createBookmarkUrlEditText.setText(urlString)
157 // Allow the enter key on the keyboard to create the bookmark from the create bookmark name edit text.
158 createBookmarkNameEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
159 // Check the key code and event.
160 if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) { // The event is a key-down on the enter key.
161 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
162 createBookmarkListener.createBookmark(this, favoriteIconBitmap)
164 // Manually dismiss the alert dialog.
165 alertDialog.dismiss()
167 // Consume the event.
168 return@setOnKeyListener true
169 } else { // Some other key was pressed.
170 // Do not consume the event.
171 return@setOnKeyListener false
175 // Allow the enter key on the keyboard to create the bookmark from create bookmark URL edit text.
176 createBookmarkUrlEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
177 // Check the key code and event.
178 if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) { // The event is a key-down on the enter key.
179 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
180 createBookmarkListener.createBookmark(this, favoriteIconBitmap)
182 // Manually dismiss the alert dialog.
183 alertDialog.dismiss()
185 // Consume the event.
186 return@setOnKeyListener true
187 } else { // Some other key was pressed.
188 // Do not consume the event.
189 return@setOnKeyListener false
193 // Return the alert dialog.