]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt
83670bc8923b66b1ddb5fc0f8244b6efc042287f
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / CreateBookmarkFolderDialog.kt
1 /*
2  * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 package com.stoutner.privacybrowser.dialogs
20
21 import android.annotation.SuppressLint
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 androidx.appcompat.app.AlertDialog
36
37 import androidx.fragment.app.DialogFragment
38 import androidx.preference.PreferenceManager
39
40 import com.stoutner.privacybrowser.R
41 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
42
43 import java.io.ByteArrayOutputStream
44
45 class CreateBookmarkFolderDialog: DialogFragment() {
46     // The public interface is used to send information back to the parent activity.
47     interface CreateBookmarkFolderListener {
48         fun onCreateBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
49     }
50
51     // The create bookmark folder listener is initialized in `onAttach()` and used in `onCreateDialog()`.
52     private lateinit var createBookmarkFolderListener: CreateBookmarkFolderListener
53
54     override fun onAttach(context: Context) {
55         // Run the default commands.
56         super.onAttach(context)
57
58         // Get a handle for the create bookmark folder listener from the launching context.
59         createBookmarkFolderListener = context as CreateBookmarkFolderListener
60     }
61
62     companion object {
63         // `@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         @JvmStatic
65         fun createBookmarkFolder(favoriteIconBitmap: Bitmap): CreateBookmarkFolderDialog {
66             // Create a favorite icon byte array output stream.
67             val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
68
69             // 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).
70             favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
71
72             // Convert the byte array output stream to a byte array.
73             val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
74
75             // Create an arguments bundle.
76             val argumentsBundle = Bundle()
77
78             // Store the favorite icon in the bundle.
79             argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
80
81             // Create a new instance of the dialog.
82             val createBookmarkFolderDialog = CreateBookmarkFolderDialog()
83
84             // Add the bundle to the dialog.
85             createBookmarkFolderDialog.arguments = argumentsBundle
86
87             // Return the new dialog.
88             return createBookmarkFolderDialog
89         }
90     }
91
92     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
93     @SuppressLint("InflateParams")
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.  The parent view is null because it will be assigned by the alert dialog.
111         dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.create_bookmark_folder_dialog, null))
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 a listener on the create button.
117         dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
118             // Return the dialog fragment to the parent activity on create.
119             createBookmarkFolderListener.onCreateBookmarkFolder(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(context)
127
128         // Get the screenshot preference.
129         val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", 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 webPageIconImageView = alertDialog.findViewById<ImageView>(R.id.create_folder_web_page_icon)!!
144         val folderNameEditText = alertDialog.findViewById<EditText>(R.id.create_folder_name_edittext)!!
145         val createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
146
147         // Display the current favorite icon.
148         webPageIconImageView.setImageBitmap(favoriteIconBitmap)
149
150         // Initially disable the create button.
151         createButton.isEnabled = false
152
153         // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
154         val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
155
156         // Enable the create button if the new folder name is unique.
157         folderNameEditText.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(editable: Editable) {
167                 // Convert the current text to a string.
168                 val folderName = editable.toString()
169
170                 // Check if a folder with the name already exists.
171                 val folderExistsCursor = bookmarksDatabaseHelper.getFolder(folderName)
172
173                 // Enable the create button if the new folder name is not empty and doesn't already exist.
174                 createButton.isEnabled = folderName.isNotEmpty() && (folderExistsCursor.count == 0)
175             }
176         })
177
178         // Set the enter key on the keyboard to create the folder from the edit text.
179         folderNameEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
180             // Check the key code, event, and button status.
181             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.
182                 // Trigger the create bookmark folder listener and return the dialog fragment to the parent activity.
183                 createBookmarkFolderListener.onCreateBookmarkFolder(this, favoriteIconBitmap)
184
185                 // Manually dismiss the alert dialog.
186                 alertDialog.dismiss()
187
188                 // Consume the event.
189                 return@setOnKeyListener true
190             } else {  // Some other key was pressed or the create button is disabled.
191                 return@setOnKeyListener false
192             }
193         }
194
195         // Return the alert dialog.
196         return alertDialog
197     }
198 }