]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / HttpAuthenticationDialog.kt
1 /*
2  * Copyright © 2017-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.os.Bundle
25 import android.text.SpannableStringBuilder
26 import android.text.Spanned
27 import android.text.style.ForegroundColorSpan
28 import android.view.KeyEvent
29 import android.view.View
30 import android.view.WindowManager
31 import android.webkit.HttpAuthHandler
32 import android.widget.EditText
33 import android.widget.TextView
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 import com.stoutner.privacybrowser.activities.MainWebViewActivity
41 import com.stoutner.privacybrowser.views.NestedScrollWebView
42
43 // Define the class constants.
44 private const val HOST = "host"
45 private const val REALM = "realm"
46 private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id"
47
48 class HttpAuthenticationDialog : DialogFragment() {
49     companion object {
50         fun displayDialog(host: String, realm: String, webViewFragmentId: Long): HttpAuthenticationDialog {
51             // Create an arguments bundle.
52             val argumentsBundle = Bundle()
53
54             // Store the variables in the bundle.
55             argumentsBundle.putString(HOST, host)
56             argumentsBundle.putString(REALM, realm)
57             argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId)
58
59             // Create a new instance of the HTTP authentication dialog.
60             val httpAuthenticationDialog = HttpAuthenticationDialog()
61
62             // Add the arguments bundle to the dialog.
63             httpAuthenticationDialog.arguments = argumentsBundle
64
65             // Return the new dialog.
66             return httpAuthenticationDialog
67         }
68     }
69
70     // Define the class variables.
71     private var httpAuthHandler: HttpAuthHandler? = null
72
73     // Declare the class views.
74     private lateinit var usernameEditText: EditText
75     private lateinit var passwordEditText: EditText
76
77     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
78         // Get a handle for the arguments.
79         val arguments = requireArguments()
80
81         // Get the variables from the bundle.
82         val httpAuthHost = arguments.getString(HOST)
83         val httpAuthRealm = arguments.getString(REALM)
84         val webViewFragmentId = arguments.getLong(WEBVIEW_FRAGMENT_ID)
85
86         // Try to populate the alert dialog.
87         try {  // Getting the WebView tab fragment will fail if Privacy Browser has been restarted.
88             // Get the current position of this WebView fragment.
89             val webViewPosition = MainWebViewActivity.webViewStateAdapter!!.getPositionForId(webViewFragmentId)
90
91             // Get the WebView tab fragment.
92             val webViewTabFragment = MainWebViewActivity.webViewStateAdapter!!.getPageFragment(webViewPosition)
93
94             // Get the fragment view.
95             val fragmentView = webViewTabFragment.requireView()
96
97             // Get a handle for the current WebView.
98             val nestedScrollWebView = fragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
99
100             // Get a handle for the HTTP authentication handler.
101             httpAuthHandler = nestedScrollWebView.httpAuthHandler
102
103             // Use an alert dialog builder to create the alert dialog.
104             val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
105
106             // Set the icon.
107             dialogBuilder.setIcon(R.drawable.lock)
108
109             // Set the title.
110             dialogBuilder.setTitle(R.string.http_authentication)
111
112             // Set the view.
113             dialogBuilder.setView(R.layout.http_authentication_dialog)
114
115             // Set the close button listener.
116             dialogBuilder.setNegativeButton(R.string.close) { _: DialogInterface?, _: Int ->
117                 if (httpAuthHandler != null) {
118                     // Cancel the HTTP authentication request.
119                     httpAuthHandler!!.cancel()
120
121                     // Reset the HTTP authentication handler.
122                     nestedScrollWebView.resetHttpAuthHandler()
123                 }
124             }
125
126             // Set the proceed button listener.
127             dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int ->
128                 // Send the login information
129                 login(httpAuthHandler, nestedScrollWebView)
130             }
131
132             // Create an alert dialog from the alert dialog builder.
133             val alertDialog = dialogBuilder.create()
134
135             // Get the alert dialog window.
136             val dialogWindow = alertDialog.window!!
137
138             // Get a handle for the shared preferences.
139             val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
140
141             // Get the screenshot preference.
142             val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
143
144             // Disable screenshots if not allowed.
145             if (!allowScreenshots) {
146                 alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
147             }
148
149             // Display the keyboard.
150             dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
151
152             // The alert dialog needs to be shown before the contents can be modified.
153             alertDialog.show()
154
155             // Get handles for the views.
156             val realmTextView = alertDialog.findViewById<TextView>(R.id.http_authentication_realm)!!
157             val hostTextView = alertDialog.findViewById<TextView>(R.id.http_authentication_host)!!
158             usernameEditText = alertDialog.findViewById(R.id.http_authentication_username)!!
159             passwordEditText = alertDialog.findViewById(R.id.http_authentication_password)!!
160
161             // Set the realm text.
162             realmTextView.text = httpAuthRealm
163
164             // Initialize the host label and the spannable string builder.
165             val hostLabel = getString(R.string.host)
166             val hostStringBuilder = SpannableStringBuilder(hostLabel + httpAuthHost)
167
168             // Set the blue color span.
169             val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.blue_text))
170
171             // Setup the span to display the host name in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
172             hostStringBuilder.setSpan(blueColorSpan, hostLabel.length, hostStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
173
174             // Set the host text.
175             hostTextView.text = hostStringBuilder
176
177             // Allow the enter key on the keyboard to send the login information from the username edit text.
178             usernameEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
179                 // Check the key code and event.
180                 if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN) {  // The enter key was pressed.
181                     // Send the login information.
182                     login(httpAuthHandler, nestedScrollWebView)
183
184                     // Manually dismiss the alert dialog.
185                     alertDialog.dismiss()
186
187                     // Consume the event.
188                     return@setOnKeyListener true
189                 } else {  // If any other key was pressed, do not consume the event.
190                     return@setOnKeyListener false
191                 }
192             }
193
194             // Allow the enter key on the keyboard to send the login information from the password edit text.
195             passwordEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
196                 // Check the key code and event.
197                 if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN) {  // The enter key was pressed.
198                     // Send the login information.
199                     login(httpAuthHandler, nestedScrollWebView)
200
201                     // Manually dismiss the alert dialog.
202                     alertDialog.dismiss()
203
204                     // Consume the event.
205                     return@setOnKeyListener true
206                 } else {  // If any other key was pressed, do not consume the event.
207                     return@setOnKeyListener false
208                 }
209             }
210
211             // Return the alert dialog.
212             return alertDialog
213         } catch (exception: Exception) {  // Privacy Browser was restarted and the HTTP auth handler no longer exists.
214             // Dismiss this new instance of the dialog as soon as it is displayed.
215             dismiss()
216
217             // Use an alert dialog builder to create an empty alert dialog.
218             val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
219
220             // Return the alert dialog.
221             return dialogBuilder.create()
222         }
223     }
224
225     override fun onSaveInstanceState(outState: Bundle) {
226         // Run the default commands.
227         super.onSaveInstanceState(outState)
228
229         // Cancel the request if the SSL error handler is not null.  This resets the WebView so it is not waiting on a response to the error handler if it is restarted in the background.
230         // Otherwise, after restart, the dialog is no longer displayed, but the error handler is still pending and there is no way to cause the dialog to redisplay for that URL in that tab.
231         if (httpAuthHandler != null)
232             httpAuthHandler!!.cancel()
233     }
234
235     private fun login(httpAuthHandler: HttpAuthHandler?, nestedScrollWebView: NestedScrollWebView) {
236         if (httpAuthHandler != null) {
237             // Send the login information.
238             httpAuthHandler.proceed(usernameEditText.text.toString(), passwordEditText.text.toString())
239
240             // Reset the HTTP authentication handler.
241             nestedScrollWebView.resetHttpAuthHandler()
242         }
243     }
244 }