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