2 * Copyright © 2016-2020 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.graphics.Bitmap
28 import android.graphics.BitmapFactory
29 import android.graphics.drawable.BitmapDrawable
30 import android.graphics.drawable.Drawable
31 import android.os.Bundle
32 import android.view.KeyEvent
33 import android.view.View
34 import android.view.WindowManager
35 import android.widget.EditText
37 import androidx.fragment.app.DialogFragment
38 import androidx.preference.PreferenceManager
40 import com.stoutner.privacybrowser.R
42 import java.io.ByteArrayOutputStream
44 class CreateBookmarkDialog: DialogFragment() {
45 // The public interface is used to send information back to the parent activity.
46 interface CreateBookmarkListener {
47 fun onCreateBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
50 // The create bookmark listener is initialized in `onAttach()` and used in `onCreateDialog()`.
51 private lateinit var createBookmarkListener: CreateBookmarkListener
53 override fun onAttach(context: Context) {
54 // Run the default commands.
55 super.onAttach(context)
57 // Get a handle for the create bookmark listener from the launching context.
58 createBookmarkListener = context as CreateBookmarkListener
62 // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function.
64 fun createBookmark(urlString: String, titleString: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog {
65 // Create a favorite icon byte array output stream.
66 val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
68 // 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).
69 favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
71 // Convert the byte array output stream to a byte array.
72 val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
74 // Create an arguments bundle.
75 val argumentsBundle = Bundle()
77 // Store the variables in the bundle.
78 argumentsBundle.putString("url_string", urlString)
79 argumentsBundle.putString("title_string", titleString)
80 argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
82 // Create a new instance of the dialog.
83 val createBookmarkDialog = CreateBookmarkDialog()
85 // Add the bundle to the dialog.
86 createBookmarkDialog.arguments = argumentsBundle
88 // Return the new dialog.
89 return createBookmarkDialog
93 // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
94 @SuppressLint("InflateParams")
95 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
97 val arguments = arguments!!
99 // Get the contents of the arguments.
100 val urlString = arguments.getString("url_string")
101 val titleString = arguments.getString("title_string")
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 // Get a handle for the shared preferences.
108 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
110 // Get the screenshot and theme preferences.
111 val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
112 val darkTheme = sharedPreferences.getBoolean("dark_theme", false)
114 // Use an alert dialog builder to create the dialog and set the style according to the theme.
115 val dialogBuilder = if (darkTheme) {
116 AlertDialog.Builder(context, R.style.PrivacyBrowserAlertDialogDark)
118 AlertDialog.Builder(context, R.style.PrivacyBrowserAlertDialogLight)
122 dialogBuilder.setTitle(R.string.create_bookmark)
124 // Create a drawable version of the favorite icon.
125 val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
128 dialogBuilder.setIcon(favoriteIconDrawable)
130 // Set the view. The parent view is `null` because it will be assigned by the alert dialog.
131 dialogBuilder.setView(activity!!.layoutInflater.inflate(R.layout.create_bookmark_dialog, null))
133 // Set a listener on the cancel button. Using `null` as the listener closes the dialog without doing anything else.
134 dialogBuilder.setNegativeButton(R.string.cancel, null)
136 // Set a listener on the create button.
137 dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
138 // Return the dialog fragment and the favorite icon bitmap to the parent activity.
139 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
142 // Create an alert dialog from the builder.
143 val alertDialog = dialogBuilder.create()
145 // Disable screenshots if not allowed.
146 if (!allowScreenshots) {
147 alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
150 // The alert dialog needs to be shown before the contents can be modified.
153 // Get a handle for the edit texts.
154 val createBookmarkNameEditText: EditText = alertDialog.findViewById(R.id.create_bookmark_name_edittext)
155 val createBookmarkUrlEditText: EditText = alertDialog.findViewById(R.id.create_bookmark_url_edittext)
157 // Set the initial texts for the edit texts.
158 createBookmarkNameEditText.setText(titleString)
159 createBookmarkUrlEditText.setText(urlString)
161 // Allow the enter key on the keyboard to create the bookmark from the create bookmark name edit text.
162 createBookmarkNameEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
163 // Check the key code and event.
164 if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) { // The event is a key-down on the enter key.
165 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
166 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
168 // Manually dismiss the alert dialog.
169 alertDialog.dismiss()
171 // Consume the event.
172 return@setOnKeyListener true
173 } else { // Some other key was pressed.
174 // Do not consume the event.
175 return@setOnKeyListener false
179 // Allow the enter key on the keyboard to create the bookmark from create bookmark URL edit text.
180 createBookmarkUrlEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
181 // Check the key code and event.
182 if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) { // The event is a key-down on the enter key.
183 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
184 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
186 // Manually dismiss the alert dialog.
187 alertDialog.dismiss()
189 // Consume the event.
190 return@setOnKeyListener true
191 } else { // Some other key was pressed.
192 // Do not consume the event.
193 return@setOnKeyListener false
197 // Return the alert dialog.