]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt
Migrate the remaining classes to Kotlin. https://redmine.stoutner.com/issues/989
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / CreateHomeScreenShortcutDialog.kt
1 /*
2  * Copyright © 2015-2023 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacybrowser.dialogs
21
22 import android.app.Dialog
23 import android.content.DialogInterface
24 import android.content.Intent
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.net.Uri
30 import android.os.Bundle
31 import android.text.Editable
32 import android.text.TextWatcher
33 import android.view.KeyEvent
34 import android.view.View
35 import android.view.WindowManager
36 import android.widget.Button
37 import android.widget.EditText
38 import android.widget.RadioButton
39
40 import androidx.appcompat.app.AlertDialog
41 import androidx.core.content.pm.ShortcutInfoCompat
42 import androidx.core.content.pm.ShortcutManagerCompat
43 import androidx.core.graphics.drawable.IconCompat
44 import androidx.fragment.app.DialogFragment
45 import androidx.preference.PreferenceManager
46
47 import com.stoutner.privacybrowser.BuildConfig
48 import com.stoutner.privacybrowser.R
49
50 import java.io.ByteArrayOutputStream
51
52 // Define the private class constants.
53 private const val SHORTCUT_NAME = "shortcut_name"
54 private const val URL_STRING = "url_string"
55 private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
56
57 class CreateHomeScreenShortcutDialog : DialogFragment() {
58     companion object {
59         fun createDialog(shortcutName: String, urlString: String, favoriteIconBitmap: Bitmap): CreateHomeScreenShortcutDialog {
60             // Create a favorite icon byte array output stream.
61             val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
62
63             // 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).
64             favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
65
66             // Convert the favorite icon byte array output stream to a byte array.
67             val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
68
69             // Create an arguments bundle.
70             val argumentsBundle = Bundle()
71
72             // Store the variables in the bundle.
73             argumentsBundle.putString(SHORTCUT_NAME, shortcutName)
74             argumentsBundle.putString(URL_STRING, urlString)
75             argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
76
77             // Create a new instance of the dialog.
78             val createHomeScreenShortcutDialog = CreateHomeScreenShortcutDialog()
79
80             // Add the bundle to the dialog.
81             createHomeScreenShortcutDialog.arguments = argumentsBundle
82
83             // Return the new dialog.
84             return createHomeScreenShortcutDialog
85         }
86     }
87
88     // Declare the class variables.
89     private lateinit var shortcutNameEditText: EditText
90     private lateinit var urlEditText: EditText
91     private lateinit var openWithPrivacyBrowserRadioButton: RadioButton
92
93     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
94         // Get the arguments.
95         val arguments = requireArguments()
96
97         // Get the variables from the arguments.
98         val initialShortcutName = arguments.getString(SHORTCUT_NAME)
99         val initialUrlString = arguments.getString(URL_STRING)
100         val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
101
102         // Convert the favorite icon byte array to a bitmap.
103         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
104
105         // Create a drawable version of the favorite icon.
106         val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
107
108         // Use an alert dialog builder to create the dialog.
109         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
110
111         // Set the title.
112         dialogBuilder.setTitle(R.string.create_shortcut)
113
114         // Set the icon.
115         dialogBuilder.setIcon(favoriteIconDrawable)
116
117         // Set the view.
118         dialogBuilder.setView(R.layout.create_home_screen_shortcut_dialog)
119
120         // Set a listener on the close button.  Using null closes the dialog without doing anything else.
121         dialogBuilder.setNegativeButton(R.string.cancel, null)
122
123         // Set a listener on the create button.
124         dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
125             // Create the home screen shortcut.
126             createHomeScreenShortcut(favoriteIconBitmap)
127         }
128
129         // Create an alert dialog from the alert dialog builder.
130         val alertDialog = dialogBuilder.create()
131
132         // Get a handle for the shared preferences.
133         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
134
135         // Get the screenshot preference.
136         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
137
138         // Disable screenshots if not allowed.
139         if (!allowScreenshots) {
140             alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
141         }
142
143         // The alert dialog must be shown before the contents can be modified.
144         alertDialog.show()
145
146         // Get handles for the views.
147         shortcutNameEditText = alertDialog.findViewById(R.id.shortcut_name_edittext)!!
148         urlEditText = alertDialog.findViewById(R.id.url_edittext)!!
149         openWithPrivacyBrowserRadioButton = alertDialog.findViewById(R.id.open_with_privacy_browser_radiobutton)!!
150         val createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
151
152         // Populate the edit texts.
153         shortcutNameEditText.setText(initialShortcutName)
154         urlEditText.setText(initialUrlString)
155
156         // Add a text change listener to the shortcut name edit text.
157         shortcutNameEditText.addTextChangedListener(object: TextWatcher {
158             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
159                 // Do nothing.
160             }
161
162             override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
163                 // Do nothing.
164             }
165
166             override fun afterTextChanged(s: Editable) {
167                 // Update the create button.
168                 updateCreateButton(createButton)
169             }
170         })
171
172         // Add a text change listener to the URL edit text.
173         urlEditText.addTextChangedListener(object : TextWatcher {
174             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
175                 // Do nothing.
176             }
177
178             override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
179                 // Do nothing.
180             }
181
182             override fun afterTextChanged(s: Editable) {
183                 // Update the create button.
184                 updateCreateButton(createButton)
185             }
186         })
187
188         // Allow the enter key on the keyboard to create the shortcut when editing the name.
189         shortcutNameEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
190             // Check the key code, event, and button status.
191             if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && createButton.isEnabled) {  // The event is a key-down on the enter key and the create button is enabled.
192                 // Create the home screen shortcut.
193                 createHomeScreenShortcut(favoriteIconBitmap)
194
195                 // Manually dismiss the alert dialog.
196                 alertDialog.dismiss()
197
198                 // Consume the event.
199                 return@setOnKeyListener true
200             } else {  // Some other key was pressed or the create button is disabled.
201                 // Do not consume the event.
202                 return@setOnKeyListener false
203             }
204         }
205
206         // Set the enter key on the keyboard to create the shortcut when editing the URL.
207         urlEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
208             // Check the key code, event, and button status.
209             if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && createButton.isEnabled) {  // The event is a key-down on the enter key and the create button is enabled.
210                 // Create the home screen shortcut.
211                 createHomeScreenShortcut(favoriteIconBitmap)
212
213                 // Manually dismiss the alert dialog.
214                 alertDialog.dismiss()
215
216                 // Consume the event.
217                 return@setOnKeyListener true
218             } else {  // Some other key was pressed or the create button is disabled.
219                 // Do not consume the event.
220                 return@setOnKeyListener false
221             }
222         }
223
224         // Return the alert dialog.
225         return alertDialog
226     }
227
228     private fun updateCreateButton(createButton: Button) {
229         // Get the contents of the edit texts.
230         val shortcutName = shortcutNameEditText.text.toString()
231         val urlString = urlEditText.text.toString()
232
233         // Enable the create button if both the shortcut name and the URL are not empty.
234         createButton.isEnabled = shortcutName.isNotEmpty() && urlString.isNotEmpty()
235     }
236
237     private fun createHomeScreenShortcut(favoriteIconBitmap: Bitmap) {
238         // Get the strings from the edit texts.
239         val shortcutName = shortcutNameEditText.text.toString()
240         val urlString = urlEditText.text.toString()
241
242         // Convert the favorite icon bitmap to an icon.  `IconCompat` must be used until the minimum API >= 26.
243         val favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap)
244
245         // Create a shortcut intent.
246         val shortcutIntent = Intent(Intent.ACTION_VIEW)
247
248         // Check to see if the shortcut should open up Privacy Browser explicitly.
249         if (openWithPrivacyBrowserRadioButton.isChecked) {
250             // Set the current application ID as the target package.
251             shortcutIntent.setPackage(BuildConfig.APPLICATION_ID)
252         }
253
254         // Add the URL to the intent.
255         shortcutIntent.data = Uri.parse(urlString)
256
257         // Create a shortcut info builder.  The shortcut name becomes the shortcut ID.
258         val shortcutInfoBuilder = ShortcutInfoCompat.Builder(requireContext(), shortcutName)
259
260         // Add the required fields to the shortcut info builder.
261         shortcutInfoBuilder.setIcon(favoriteIcon)
262         shortcutInfoBuilder.setIntent(shortcutIntent)
263         shortcutInfoBuilder.setShortLabel(shortcutName)
264
265         // Add the shortcut to the home screen.  `ShortcutManagerCompat` can be switched to `ShortcutManager` once the minimum API >= 26.
266         ShortcutManagerCompat.requestPinShortcut(requireContext(), shortcutInfoBuilder.build(), null)
267     }
268 }