]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt
Allow duplicate bookmark folders. https://redmine.stoutner.com/issues/199
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / CreateBookmarkFolderDialog.kt
1 /*
2  * Copyright © 2016-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.Context
24 import android.content.DialogInterface
25 import android.graphics.Bitmap
26 import android.graphics.BitmapFactory
27 import android.os.Bundle
28 import android.text.Editable
29 import android.text.TextWatcher
30 import android.view.KeyEvent
31 import android.view.View
32 import android.view.WindowManager
33 import android.widget.EditText
34 import android.widget.ImageView
35 import android.widget.LinearLayout
36 import android.widget.RadioButton
37
38 import androidx.appcompat.app.AlertDialog
39 import androidx.fragment.app.DialogFragment
40 import androidx.preference.PreferenceManager
41
42 import com.stoutner.privacybrowser.R
43
44 import java.io.ByteArrayOutputStream
45
46 // Define the class constants.
47 private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
48
49 class CreateBookmarkFolderDialog : DialogFragment() {
50     companion object {
51         fun createBookmarkFolder(favoriteIconBitmap: Bitmap): CreateBookmarkFolderDialog {
52             // Create a favorite icon byte array output stream.
53             val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
54
55             // 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).
56             favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
57
58             // Convert the byte array output stream to a byte array.
59             val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
60
61             // Create an arguments bundle.
62             val argumentsBundle = Bundle()
63
64             // Store the favorite icon in the bundle.
65             argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
66
67             // Create a new instance of the dialog.
68             val createBookmarkFolderDialog = CreateBookmarkFolderDialog()
69
70             // Add the bundle to the dialog.
71             createBookmarkFolderDialog.arguments = argumentsBundle
72
73             // Return the new dialog.
74             return createBookmarkFolderDialog
75         }
76     }
77
78     // Declare the class variables.
79     private lateinit var createBookmarkFolderListener: CreateBookmarkFolderListener
80
81     // The public interface is used to send information back to the parent activity.
82     interface CreateBookmarkFolderListener {
83         fun createBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
84     }
85
86     override fun onAttach(context: Context) {
87         // Run the default commands.
88         super.onAttach(context)
89
90         // Get a handle for the create bookmark folder listener from the launching context.
91         createBookmarkFolderListener = context as CreateBookmarkFolderListener
92     }
93
94     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
95         // Get the arguments.
96         val arguments = requireArguments()
97
98         // Get the favorite icon byte array.
99         val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
100
101         // Convert the favorite icon byte array to a bitmap.
102         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
103
104         // Use an alert dialog builder to create the dialog.
105         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
106
107         // Set the title.
108         dialogBuilder.setTitle(R.string.create_folder)
109
110         // Set the view.
111         dialogBuilder.setView(R.layout.create_bookmark_folder_dialog)
112
113         // Set a listener on the cancel button.  Using `null` as the listener closes the dialog without doing anything else.
114         dialogBuilder.setNegativeButton(R.string.cancel, null)
115
116         // Set the create button listener.
117         dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
118             // Return the dialog fragment to the parent activity on create.
119             createBookmarkFolderListener.createBookmarkFolder(this, favoriteIconBitmap)
120         }
121
122         // Create an alert dialog from the builder.
123         val alertDialog = dialogBuilder.create()
124
125         // Get a handle for the shared preferences.
126         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
127
128         // Get the screenshot preference.
129         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
130
131         // Disable screenshots if not allowed.
132         if (!allowScreenshots) {
133             alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
134         }
135
136         // Display the keyboard.
137         alertDialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
138
139         // The alert dialog must be shown before the content can be modified.
140         alertDialog.show()
141
142         // Get handles for the views in the dialog.
143         val defaultIconLinearLayout = alertDialog.findViewById<LinearLayout>(R.id.default_icon_linearlayout)!!
144         val defaultIconRadioButton = alertDialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)!!
145         val webpageFavoriteIconLinearLayout = alertDialog.findViewById<LinearLayout>(R.id.webpage_favorite_icon_linearlayout)!!
146         val webpageFavoriteIconRadioButton = alertDialog.findViewById<RadioButton>(R.id.webpage_favorite_icon_radiobutton)!!
147         val webpageFavoriteIconImageView = alertDialog.findViewById<ImageView>(R.id.webpage_favorite_icon_imageview)!!
148         val folderNameEditText = alertDialog.findViewById<EditText>(R.id.folder_name_edittext)!!
149         val createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
150
151         // Display the current favorite icon.
152         webpageFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
153
154         // Initially disable the create button.
155         createButton.isEnabled = false
156
157         // Set the radio button listeners.  These perform a click on the linear layout, which contains the necessary logic.
158         defaultIconRadioButton.setOnClickListener { defaultIconLinearLayout.performClick() }
159         webpageFavoriteIconRadioButton.setOnClickListener { webpageFavoriteIconLinearLayout.performClick() }
160
161         // Set the default icon linear layout click listener.
162         defaultIconLinearLayout.setOnClickListener {
163             // Check the default icon radio button.
164             defaultIconRadioButton.isChecked = true
165
166             // Uncheck the webpage favorite icon radio button.
167             webpageFavoriteIconRadioButton.isChecked = false
168         }
169
170         // Set the webpage favorite icon linear layout click listener.
171         webpageFavoriteIconLinearLayout.setOnClickListener {
172             // Check the webpage favorite icon radio button.
173             webpageFavoriteIconRadioButton.isChecked = true
174
175             // Uncheck the default icon radio button.
176             defaultIconRadioButton.isChecked = false
177         }
178
179         // Enable the create button if the folder name is populated.
180         folderNameEditText.addTextChangedListener(object: TextWatcher {
181             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
182                 // Do nothing.
183             }
184
185             override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
186                 // Do nothing.
187             }
188
189             override fun afterTextChanged(editable: Editable) {
190                 // Convert the current text to a string.
191                 val folderName = editable.toString()
192
193                 // Enable the create button if the new folder name is not empty.
194                 createButton.isEnabled = folderName.isNotEmpty()
195             }
196         })
197
198         // Set the enter key on the keyboard to create the folder from the edit text.
199         folderNameEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
200             // Check the key code, event, and button status.
201             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.
202                 // Trigger the create bookmark folder listener and return the dialog fragment to the parent activity.
203                 createBookmarkFolderListener.createBookmarkFolder(this, favoriteIconBitmap)
204
205                 // Manually dismiss the alert dialog.
206                 alertDialog.dismiss()
207
208                 // Consume the event.
209                 return@setOnKeyListener true
210             } else {  // Some other key was pressed or the create button is disabled.
211                 return@setOnKeyListener false
212             }
213         }
214
215         // Return the alert dialog.
216         return alertDialog
217     }
218 }