]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
Switch to the new Day/Night theme. https://redmine.stoutner.com/issues/522
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / ViewSourceActivity.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.activities;
21
22 import android.app.Activity;
23 import android.content.Context;
24 import android.content.Intent;
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.Spanned;
30 import android.text.style.ForegroundColorSpan;
31 import android.util.TypedValue;
32 import android.view.KeyEvent;
33 import android.view.Menu;
34 import android.view.MenuItem;
35 import android.view.View;
36 import android.view.WindowManager;
37 import android.view.inputmethod.InputMethodManager;
38 import android.widget.EditText;
39
40 import androidx.annotation.NonNull;
41 import androidx.appcompat.app.ActionBar;
42 import androidx.appcompat.app.AppCompatActivity;
43 import androidx.appcompat.widget.Toolbar;  // The AndroidX toolbar must be used until the minimum API is >= 21.
44 import androidx.core.app.NavUtils;
45 import androidx.fragment.app.DialogFragment;
46 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
47
48 import com.stoutner.privacybrowser.R;
49 import com.stoutner.privacybrowser.asynctasks.GetSource;
50 import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog;
51
52 public class ViewSourceActivity extends AppCompatActivity {
53     // `activity` is used in `onCreate()` and `goBack()`.
54     private Activity activity;
55
56     // The color spans are used in `onCreate()` and `highlightUrlText()`.
57     private ForegroundColorSpan redColorSpan;
58     private ForegroundColorSpan initialGrayColorSpan;
59     private ForegroundColorSpan finalGrayColorSpan;
60
61     @Override
62     protected void onCreate(Bundle savedInstanceState) {
63         // Get a handle for the shared preferences.
64         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
65
66         // Get the screenshot preference.
67         boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
68
69         // Disable screenshots if not allowed.
70         if (!allowScreenshots) {
71             getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
72         }
73
74         // Set the theme.
75         setTheme(R.style.PrivacyBrowser);
76
77         // Run the default commands.
78         super.onCreate(savedInstanceState);
79
80         // Get the launching intent
81         Intent intent = getIntent();
82
83         // Get the information from the intent.
84         String userAgent = intent.getStringExtra("user_agent");
85         String currentUrl = intent.getStringExtra("current_url");
86
87         // Store a handle for the current activity.
88         activity = this;
89
90         // Set the content view.
91         setContentView(R.layout.view_source_coordinatorlayout);
92
93         // The AndroidX toolbar must be used until the minimum API is >= 21.
94         Toolbar toolbar = findViewById(R.id.view_source_toolbar);
95         setSupportActionBar(toolbar);
96
97         // Get a handle for the action bar.
98         final ActionBar actionBar = getSupportActionBar();
99
100         // Remove the incorrect lint warning that the action bar might be null.
101         assert actionBar != null;
102
103         // Add the custom layout to the action bar.
104         actionBar.setCustomView(R.layout.view_source_app_bar);
105         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
106
107         // Get a handle for the url text box.
108         EditText urlEditText = findViewById(R.id.url_edittext);
109
110         // Populate the URL text box.
111         urlEditText.setText(currentUrl);
112
113         // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
114         redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
115         initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
116         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
117
118         // Apply text highlighting to the URL.
119         highlightUrlText();
120
121         // Get a handle for the input method manager, which is used to hide the keyboard.
122         InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
123
124         // Remove the lint warning that the input method manager might be null.
125         assert inputMethodManager != null;
126
127         // Remove the formatting from the URL when the user is editing the text.
128         urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
129             if (hasFocus) {  // The user is editing `urlTextBox`.
130                 // Remove the highlighting.
131                 urlEditText.getText().removeSpan(redColorSpan);
132                 urlEditText.getText().removeSpan(initialGrayColorSpan);
133                 urlEditText.getText().removeSpan(finalGrayColorSpan);
134             } else {  // The user has stopped editing `urlTextBox`.
135                 // Hide the soft keyboard.
136                 inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
137
138                 // Move to the beginning of the string.
139                 urlEditText.setSelection(0);
140
141                 // Reapply the highlighting.
142                 highlightUrlText();
143             }
144         });
145
146         // Set the go button on the keyboard to request new source data.
147         urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
148             // Request new source data if the enter key was pressed.
149             if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
150                 // Hide the soft keyboard.
151                 inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
152
153                 // Remove the focus from the URL box.
154                 urlEditText.clearFocus();
155
156                 // Get the URL.
157                 String url = urlEditText.getText().toString();
158
159                 // Get new source data for the current URL if it beings with `http`.
160                 if (url.startsWith("http")) {
161                     new GetSource(this, this, userAgent).execute(url);
162                 }
163
164                 // Consume the key press.
165                 return true;
166             } else {
167                 // Do not consume the key press.
168                 return false;
169             }
170         });
171
172         // Get a handle for the swipe refresh layout.
173         SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.view_source_swiperefreshlayout);
174
175         // Implement swipe to refresh.
176         swipeRefreshLayout.setOnRefreshListener(() -> {
177             // Get the URL.
178             String url = urlEditText.getText().toString();
179
180             // Get new source data for the URL if it begins with `http`.
181             if (url.startsWith("http")) {
182                 new GetSource(this, this, userAgent).execute(url);
183             } else {
184                 // Stop the refresh animation.
185                 swipeRefreshLayout.setRefreshing(false);
186             }
187         });
188
189         // Get the current theme status.
190         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
191
192         // Set the refresh color scheme according to the theme.
193         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
194             swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
195         } else {
196             swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
197         }
198
199         // Initialize a color background typed value.
200         TypedValue colorBackgroundTypedValue = new TypedValue();
201
202         // Get the color background from the theme.
203         getTheme().resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true);
204
205         // Get the color background int from the typed value.
206         int colorBackgroundInt = colorBackgroundTypedValue.data;
207
208         // Set the swipe refresh background color.
209         swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt);
210
211         // Get the source using an AsyncTask if the URL begins with `http`.
212         if ((currentUrl != null) && currentUrl.startsWith("http")) {
213             new GetSource(this, this, userAgent).execute(currentUrl);
214         }
215     }
216
217     @Override
218     public boolean onCreateOptionsMenu(Menu menu) {
219         // Inflate the menu.  This adds items to the action bar if it is present.
220         getMenuInflater().inflate(R.menu.view_source_options_menu, menu);
221
222         // Display the menu.
223         return true;
224     }
225
226     @Override
227     public boolean onOptionsItemSelected(@NonNull MenuItem menuItem) {
228         // Get a handle for the about alert dialog.
229         DialogFragment aboutDialogFragment = new AboutViewSourceDialog();
230
231         // Show the about alert dialog.
232         aboutDialogFragment.show(getSupportFragmentManager(), getString(R.string.about));
233
234         // Consume the event.
235         return true;
236     }
237
238     public void goBack(View view) {
239         // Go home.
240         NavUtils.navigateUpFromSameTask(activity);
241     }
242
243     private void highlightUrlText() {
244         // Get a handle for the URL EditText.
245         EditText urlEditText = findViewById(R.id.url_edittext);
246
247         // Get the URL string.
248         String urlString = urlEditText.getText().toString();
249
250         // Highlight the URL according to the protocol.
251         if (urlString.startsWith("file://")) {  // This is a file URL.
252             // De-emphasize only the protocol.
253             urlEditText.getText().setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
254         } else if (urlString.startsWith("content://")) {
255             // De-emphasize only the protocol.
256             urlEditText.getText().setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
257         } else {  // This is a web URL.
258             // Get the index of the `/` immediately after the domain name.
259             int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2));
260
261             // Create a base URL string.
262             String baseUrl;
263
264             // Get the base URL.
265             if (endOfDomainName > 0) {  // There is at least one character after the base URL.
266                 // Get the base URL.
267                 baseUrl = urlString.substring(0, endOfDomainName);
268             } else {  // There are no characters after the base URL.
269                 // Set the base URL to be the entire URL string.
270                 baseUrl = urlString;
271             }
272
273             // Get the index of the last `.` in the domain.
274             int lastDotIndex = baseUrl.lastIndexOf(".");
275
276             // Get the index of the penultimate `.` in the domain.
277             int penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1);
278
279             // Markup the beginning of the URL.
280             if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
281                 urlEditText.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
282
283                 // De-emphasize subdomains.
284                 if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
285                     urlEditText.getText().setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
286                 }
287             } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
288                 if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
289                     // De-emphasize the protocol and the additional subdomains.
290                     urlEditText.getText().setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
291                 } else {  // There is only one subdomain in the domain name.
292                     // De-emphasize only the protocol.
293                     urlEditText.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
294                 }
295             }
296
297             // De-emphasize the text after the domain name.
298             if (endOfDomainName > 0) {
299                 urlEditText.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
300             }
301         }
302     }
303 }