Fix the ViewPager not always moving to new tabs. https://redmine.stoutner.com/issues/798
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / CreateBookmarkDialog.kt
1 /*
2  * Copyright © 2016-2022 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.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
34
35 import androidx.appcompat.app.AlertDialog
36 import androidx.fragment.app.DialogFragment
37 import androidx.preference.PreferenceManager
38
39 import com.stoutner.privacybrowser.R
40
41 import java.io.ByteArrayOutputStream
42
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"
47
48 class CreateBookmarkDialog : DialogFragment() {
49     // Declare the class variables
50     private lateinit var createBookmarkListener: CreateBookmarkListener
51
52     // The public interface is used to send information back to the parent activity.
53     interface CreateBookmarkListener {
54         fun onCreateBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
55     }
56
57     override fun onAttach(context: Context) {
58         // Run the default commands.
59         super.onAttach(context)
60
61         // Get a handle for the create bookmark listener from the launching context.
62         createBookmarkListener = context as CreateBookmarkListener
63     }
64
65     companion object {
66         // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
67         @JvmStatic
68         fun createBookmark(urlString: String, title: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog {
69             // Create a favorite icon byte array output stream.
70             val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
71
72             // 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).
73             favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
74
75             // Convert the byte array output stream to a byte array.
76             val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
77
78             // Create an arguments bundle.
79             val argumentsBundle = Bundle()
80
81             // Store the variables in the bundle.
82             argumentsBundle.putString(URL_STRING, urlString)
83             argumentsBundle.putString(TITLE, title)
84             argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
85
86             // Create a new instance of the dialog.
87             val createBookmarkDialog = CreateBookmarkDialog()
88
89             // Add the bundle to the dialog.
90             createBookmarkDialog.arguments = argumentsBundle
91
92             // Return the new dialog.
93             return createBookmarkDialog
94         }
95     }
96
97     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
98         // Get the arguments.
99         val arguments = requireArguments()
100
101         // Get the contents of the arguments.
102         val urlString = arguments.getString(URL_STRING)
103         val title = arguments.getString(TITLE)
104         val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
105
106         // Convert the favorite icon byte array to a bitmap.
107         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
108
109         // Use an alert dialog builder to create the dialog.
110         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
111
112         // Set the title.
113         dialogBuilder.setTitle(R.string.create_bookmark)
114
115         // Create a drawable version of the favorite icon.
116         val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
117
118         // Set the icon.
119         dialogBuilder.setIcon(favoriteIconDrawable)
120
121         // Set the view.
122         dialogBuilder.setView(R.layout.create_bookmark_dialog)
123
124         // Set a listener on the cancel button.  Using `null` as the listener closes the dialog without doing anything else.
125         dialogBuilder.setNegativeButton(R.string.cancel, null)
126
127         // Set a listener on the create button.
128         dialogBuilder.setPositiveButton(R.string.create) { _: DialogInterface, _: Int ->
129             // Return the dialog fragment and the favorite icon bitmap to the parent activity.
130             createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
131         }
132
133         // Create an alert dialog from the builder.
134         val alertDialog = dialogBuilder.create()
135
136
137         // Get a handle for the shared preferences.
138         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
139
140         // Get the screenshot preference.
141         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
142
143         // Disable screenshots if not allowed.
144         if (!allowScreenshots) {
145             alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
146         }
147
148         // The alert dialog needs to be shown before the contents can be modified.
149         alertDialog.show()
150
151         // Get a handle for the edit texts.
152         val createBookmarkNameEditText = alertDialog.findViewById<EditText>(R.id.create_bookmark_name_edittext)!!
153         val createBookmarkUrlEditText = alertDialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)!!
154
155         // Set the initial texts for the edit texts.
156         createBookmarkNameEditText.setText(title)
157         createBookmarkUrlEditText.setText(urlString)
158
159         // Allow the enter key on the keyboard to create the bookmark from the create bookmark name edit text.
160         createBookmarkNameEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
161             // Check the key code and event.
162             if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) {  // The event is a key-down on the enter key.
163                 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
164                 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
165
166                 // Manually dismiss the alert dialog.
167                 alertDialog.dismiss()
168
169                 // Consume the event.
170                 return@setOnKeyListener true
171             } else {  // Some other key was pressed.
172                 // Do not consume the event.
173                 return@setOnKeyListener false
174             }
175         }
176
177         // Allow the enter key on the keyboard to create the bookmark from create bookmark URL edit text.
178         createBookmarkUrlEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
179             // Check the key code and event.
180             if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN) {  // The event is a key-down on the enter key.
181                 // Trigger the create bookmark listener and return the dialog fragment and the favorite icon bitmap to the parent activity.
182                 createBookmarkListener.onCreateBookmark(this, favoriteIconBitmap)
183
184                 // Manually dismiss the alert dialog.
185                 alertDialog.dismiss()
186
187                 // Consume the event.
188                 return@setOnKeyListener true
189             } else { // Some other key was pressed.
190                 // Do not consume the event.
191                 return@setOnKeyListener false
192             }
193         }
194
195         // Return the alert dialog.
196         return alertDialog
197     }
198 }