Fix importing of settings from prior to 3.3. https://redmine.stoutner.com/issues/608
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / HttpAuthenticationDialog.java
1 /*
2  * Copyright © 2017-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
20 package com.stoutner.privacybrowser.dialogs;
21
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;
40
41 import androidx.annotation.NonNull;
42 import androidx.appcompat.app.AlertDialog;
43 import androidx.fragment.app.DialogFragment;
44
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;
49
50 public class HttpAuthenticationDialog extends DialogFragment{
51     // Define the class variables.
52     private EditText usernameEditText;
53     private EditText passwordEditText;
54
55     public static HttpAuthenticationDialog displayDialog(String host, String realm, long webViewFragmentId) {
56         // Create an arguments bundle.
57         Bundle argumentsBundle = new Bundle();
58
59         // Store the variables in the bundle.
60         argumentsBundle.putString("host", host);
61         argumentsBundle.putString("realm", realm);
62         argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
63
64         // Create a new instance of the HTTP authentication dialog.
65         HttpAuthenticationDialog thisHttpAuthenticationDialog = new HttpAuthenticationDialog();
66
67         // Add the arguments bundle to the new dialog.
68         thisHttpAuthenticationDialog.setArguments(argumentsBundle);
69
70         // Return the new dialog.
71         return thisHttpAuthenticationDialog;
72     }
73
74     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
75     @SuppressLint("InflateParams")
76     @Override
77     @NonNull
78     public Dialog onCreateDialog(Bundle savedInstanceState) {
79         // Get a handle for the arguments.
80         Bundle arguments = getArguments();
81
82         // Remove the incorrect lint warning below that arguments might be null.
83         assert arguments != null;
84
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");
89
90         // Get the current position of this WebView fragment.
91         int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
92
93         // Get the WebView tab fragment.
94         WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
95
96         // Get the fragment view.
97         View fragmentView = webViewTabFragment.getView();
98
99         // Remove the incorrect lint warning below that the fragment view might be null.
100         assert fragmentView != null;
101
102         // Get a handle for the current WebView.
103         NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
104
105         // Get a handle for the HTTP authentication handler.
106         HttpAuthHandler httpAuthHandler = nestedScrollWebView.getHttpAuthHandler();
107
108         // Remove the incorrect lint warning that `getActivity()` might be null.
109         assert getActivity() != null;
110
111         // Get the activity's layout inflater.
112         LayoutInflater layoutInflater = getActivity().getLayoutInflater();
113
114         // Use an alert dialog builder to create the alert dialog.
115         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialog);
116
117         // Get the current theme status.
118         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
119
120         // Set the icon according to the theme.
121         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
122             dialogBuilder.setIcon(R.drawable.lock_night);
123         } else {
124             dialogBuilder.setIcon(R.drawable.lock_day);
125         }
126
127         // Set the title.
128         dialogBuilder.setTitle(R.string.http_authentication);
129
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));
132
133         // Setup the close button.
134         dialogBuilder.setNegativeButton(R.string.close, (DialogInterface dialog, int which) -> {
135             // Cancel the HTTP authentication request.
136             httpAuthHandler.cancel();
137
138             // Reset the HTTP authentication handler.
139             nestedScrollWebView.resetHttpAuthHandler();
140         });
141
142         // Setup the proceed button.
143         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
144             // Send the login information
145             login(httpAuthHandler);
146
147             // Reset the HTTP authentication handler.
148             nestedScrollWebView.resetHttpAuthHandler();
149         });
150
151         // Create an alert dialog from the alert dialog builder.
152         final AlertDialog alertDialog = dialogBuilder.create();
153
154         // Get the alert dialog window.
155         Window dialogWindow = alertDialog.getWindow();
156
157         // Remove the incorrect lint warning below that the dialog window might be null.
158         assert dialogWindow != null;
159
160         // Get a handle for the shared preferences.
161         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
162
163         // Get the screenshot preference.
164         boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
165
166         // Disable screenshots if not allowed.
167         if (!allowScreenshots) {
168             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
169         }
170
171         // Display the keyboard.
172         dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
173
174         // The alert dialog needs to be shown before the contents can be modified.
175         alertDialog.show();
176
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);
182
183         // Remove the incorrect lint warnings below that the views might be null.
184         assert realmTextView != null;
185         assert hostTextView != null;
186
187         // Set the realm text.
188         realmTextView.setText(httpAuthRealm);
189
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));
193         } else {
194             realmTextView.setTextColor(getResources().getColor(R.color.black));
195         }
196
197         // Initialize the host label and the `SpannableStringBuilder`.
198         String hostLabel = getString(R.string.host) + "  ";
199         SpannableStringBuilder hostStringBuilder = new SpannableStringBuilder(hostLabel + httpAuthHost);
200
201         // Create a blue `ForegroundColorSpan`.
202         ForegroundColorSpan blueColorSpan;
203
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));
207         } else {
208             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
209         }
210
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);
213
214         // Set the host text.
215         hostTextView.setText(hostStringBuilder);
216
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);
223
224                 // Manually dismiss the alert dialog.
225                 alertDialog.dismiss();
226
227                 // Consume the event.
228                 return true;
229             } else {  // If any other key was pressed, do not consume the event.
230                 return false;
231             }
232         });
233
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);
240
241                 // Manually dismiss the alert dialog.
242                 alertDialog.dismiss();
243
244                 // Consume the event.
245                 return true;
246             } else {  // If any other key was pressed, do not consume the event.
247                 return false;
248             }
249         });
250
251         // Return the alert dialog.
252         return alertDialog;
253     }
254
255     private void login(HttpAuthHandler httpAuthHandler) {
256         // Send the login information.
257         httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
258     }
259 }