2 * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
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.
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.
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/>.
20 package com.stoutner.privacybrowser.dialogs;
22 import android.annotation.SuppressLint;
23 import android.app.Dialog;
24 import android.content.DialogInterface;
25 import android.content.SharedPreferences;
26 import android.content.res.Configuration;
27 import android.os.Bundle;
28 import android.preference.PreferenceManager;
29 import android.text.SpannableStringBuilder;
30 import android.text.Spanned;
31 import android.text.style.ForegroundColorSpan;
32 import android.view.KeyEvent;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.Window;
36 import android.view.WindowManager;
37 import android.webkit.HttpAuthHandler;
38 import android.widget.EditText;
39 import android.widget.TextView;
41 import androidx.annotation.NonNull;
42 import androidx.appcompat.app.AlertDialog;
43 import androidx.fragment.app.DialogFragment;
45 import com.stoutner.privacybrowser.R;
46 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
47 import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
48 import com.stoutner.privacybrowser.views.NestedScrollWebView;
50 public class HttpAuthenticationDialog extends DialogFragment{
51 // Define the class variables.
52 private EditText usernameEditText;
53 private EditText passwordEditText;
55 public static HttpAuthenticationDialog displayDialog(String host, String realm, long webViewFragmentId) {
56 // Create an arguments bundle.
57 Bundle argumentsBundle = new Bundle();
59 // Store the variables in the bundle.
60 argumentsBundle.putString("host", host);
61 argumentsBundle.putString("realm", realm);
62 argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
64 // Create a new instance of the HTTP authentication dialog.
65 HttpAuthenticationDialog thisHttpAuthenticationDialog = new HttpAuthenticationDialog();
67 // Add the arguments bundle to the new dialog.
68 thisHttpAuthenticationDialog.setArguments(argumentsBundle);
70 // Return the new dialog.
71 return thisHttpAuthenticationDialog;
74 // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
75 @SuppressLint("InflateParams")
78 public Dialog onCreateDialog(Bundle savedInstanceState) {
79 // Get a handle for the arguments.
80 Bundle arguments = getArguments();
82 // Remove the incorrect lint warning below that arguments might be null.
83 assert arguments != null;
85 // Get the variables from the bundle.
86 String httpAuthHost = arguments.getString("host");
87 String httpAuthRealm = arguments.getString("realm");
88 long webViewFragmentId = arguments.getLong("webview_fragment_id");
90 // Get the current position of this WebView fragment.
91 int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
93 // Get the WebView tab fragment.
94 WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
96 // Get the fragment view.
97 View fragmentView = webViewTabFragment.getView();
99 // Remove the incorrect lint warning below that the fragment view might be null.
100 assert fragmentView != null;
102 // Get a handle for the current WebView.
103 NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
105 // Get a handle for the HTTP authentication handler.
106 HttpAuthHandler httpAuthHandler = nestedScrollWebView.getHttpAuthHandler();
108 // Remove the incorrect lint warning that `getActivity()` might be null.
109 assert getActivity() != null;
111 // Get the activity's layout inflater.
112 LayoutInflater layoutInflater = getActivity().getLayoutInflater();
114 // Use an alert dialog builder to create the alert dialog.
115 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialog);
117 // Get the current theme status.
118 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
120 // Set the icon according to the theme.
121 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
122 dialogBuilder.setIcon(R.drawable.lock_night);
124 dialogBuilder.setIcon(R.drawable.lock_day);
128 dialogBuilder.setTitle(R.string.http_authentication);
130 // Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`.
131 dialogBuilder.setView(layoutInflater.inflate(R.layout.http_authentication_dialog, null));
133 // Setup the close button.
134 dialogBuilder.setNegativeButton(R.string.close, (DialogInterface dialog, int which) -> {
135 // Cancel the HTTP authentication request.
136 httpAuthHandler.cancel();
138 // Reset the HTTP authentication handler.
139 nestedScrollWebView.resetHttpAuthHandler();
142 // Setup the proceed button.
143 dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
144 // Send the login information
145 login(httpAuthHandler);
147 // Reset the HTTP authentication handler.
148 nestedScrollWebView.resetHttpAuthHandler();
151 // Create an alert dialog from the alert dialog builder.
152 final AlertDialog alertDialog = dialogBuilder.create();
154 // Get the alert dialog window.
155 Window dialogWindow = alertDialog.getWindow();
157 // Remove the incorrect lint warning below that the dialog window might be null.
158 assert dialogWindow != null;
160 // Get a handle for the shared preferences.
161 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
163 // Get the screenshot preference.
164 boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
166 // Disable screenshots if not allowed.
167 if (!allowScreenshots) {
168 alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
171 // Display the keyboard.
172 dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
174 // The alert dialog needs to be shown before the contents can be modified.
177 // Get handles for the views.
178 TextView realmTextView = alertDialog.findViewById(R.id.http_authentication_realm);
179 TextView hostTextView = alertDialog.findViewById(R.id.http_authentication_host);
180 usernameEditText = alertDialog.findViewById(R.id.http_authentication_username);
181 passwordEditText = alertDialog.findViewById(R.id.http_authentication_password);
183 // Remove the incorrect lint warnings below that the views might be null.
184 assert realmTextView != null;
185 assert hostTextView != null;
187 // Set the realm text.
188 realmTextView.setText(httpAuthRealm);
190 // Set the realm text color according to the theme. The deprecated `getResources()` must be used until API >= 23.
191 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
192 realmTextView.setTextColor(getResources().getColor(R.color.gray_300));
194 realmTextView.setTextColor(getResources().getColor(R.color.black));
197 // Initialize the host label and the `SpannableStringBuilder`.
198 String hostLabel = getString(R.string.host) + " ";
199 SpannableStringBuilder hostStringBuilder = new SpannableStringBuilder(hostLabel + httpAuthHost);
201 // Create a blue `ForegroundColorSpan`.
202 ForegroundColorSpan blueColorSpan;
204 // Set the blue color span according to the theme. The deprecated `getResources()` must be used until API >= 23.
205 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
206 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
208 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
211 // Setup the span to display the host name in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
212 hostStringBuilder.setSpan(blueColorSpan, hostLabel.length(), hostStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
214 // Set the host text.
215 hostTextView.setText(hostStringBuilder);
217 // Allow the `enter` key on the keyboard to trigger `onHttpAuthenticationProceed` from `usernameEditText`.
218 usernameEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
219 // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
220 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
221 // Send the login information.
222 login(httpAuthHandler);
224 // Manually dismiss the alert dialog.
225 alertDialog.dismiss();
227 // Consume the event.
229 } else { // If any other key was pressed, do not consume the event.
234 // Allow the `enter` key on the keyboard to trigger `onHttpAuthenticationProceed()` from `passwordEditText`.
235 passwordEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
236 // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
237 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
238 // Send the login information.
239 login(httpAuthHandler);
241 // Manually dismiss the alert dialog.
242 alertDialog.dismiss();
244 // Consume the event.
246 } else { // If any other key was pressed, do not consume the event.
251 // Return the alert dialog.
255 private void login(HttpAuthHandler httpAuthHandler) {
256 // Send the login information.
257 httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());