import java.util.Map;
import java.util.Set;
-// TODO. New tabs are white in dark mode.
-
// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
@Override
public void afterTextChanged(Editable s) {
- // Search for the text in `mainWebView`.
- currentWebView.findAllAsync(findOnPageEditText.getText().toString());
+ // Search for the text in the WebView if it is not null. Sometimes on resume after a period of non-use the WebView will be null.
+ if (currentWebView != null) {
+ currentWebView.findAllAsync(findOnPageEditText.getText().toString());
+ }
}
});
// Reset the current domain name so the domain settings will be reapplied.
nestedScrollWebView.resetCurrentDomainName();
- // Reapply the domain settings.
- applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+ // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
+ if (nestedScrollWebView.getUrl() != null) {
+ applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+ }
}
}
}
// Update the privacy icons. `true` redraws the icons in the app bar.
updatePrivacyIcons(true);
- // Clear the focus from the URL text box.
- urlEditText.clearFocus();
-
// Get a handle for the input method manager.
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// Remove the lint warning below that the input method manager might be null.
assert inputMethodManager != null;
- // Hide the soft keyboard.
- inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+ // Get the current URL.
+ String url = currentWebView.getUrl();
+
+ if ((url == null) || url.equals("about:blank")) { // The WebView is blank.
+ // Display the hint in the URL edit text.
+ urlEditText.setText("");
+
+ // Request focus for the URL text box.
+ urlEditText.requestFocus();
- // Display the current URL in the URL text box.
- urlEditText.setText(currentWebView.getUrl());
+ // Display the keyboard.
+ inputMethodManager.showSoftInput(urlEditText, 0);
+ } else { // The WebView has a loaded URL.
+ // Clear the focus from the URL text box.
+ urlEditText.clearFocus();
- // Highlight the URL text.
- highlightUrlText();
+ // Hide the soft keyboard.
+ inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+
+ // Display the current URL in the URL text box.
+ urlEditText.setText(url);
+
+ // Highlight the URL text.
+ highlightUrlText();
+ }
// Set the background to indicate the domain settings status.
if (currentWebView.getDomainSettingsApplied()) {
@Override
public void onProgressChanged(WebView view, int progress) {
// Inject the night mode CSS if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) {
+ if (nestedScrollWebView.getNightMode()) { // Night mode is enabled.
// `background-color: #212121` sets the background to be dark gray. `color: #BDBDBD` sets the text color to be light gray. `box-shadow: none` removes a lower underline on links
// used by WordPress. `text-decoration: none` removes all text underlines. `text-shadow: none` removes text shadows, which usually have a hard coded color.
// `border: none` removes all borders, which can also be used to underline text. `a {color: #1565C0}` sets links to be a dark blue.
// Display the WebView after 500 milliseconds.
displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
});
+ } else { // Night mode is disabled.
+ // Display the nested scroll WebView if night mode is disabled.
+ // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
+ // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
// Update the progress bar.
// Hide the progress bar.
progressBar.setVisibility(View.GONE);
- // Display the nested scroll WebView if night mode is disabled.
- // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
- // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
- if (!nestedScrollWebView.getNightMode()) {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
}
// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
if (nestedScrollWebView.getNightMode()) {
nestedScrollWebView.setVisibility(View.INVISIBLE);
+ } else {
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
// Hide the keyboard.
// Display the keyboard.
inputMethodManager.showSoftInput(urlEditText, 0);
- // Hide the WebView, which causes the default background color to be displayed according to the theme. // TODO
- nestedScrollWebView.setVisibility(View.GONE);
+ // Hide the WebView, which causes the default background color to be displayed according to the theme.
+ nestedScrollWebView.setVisibility(View.INVISIBLE);
// Apply the domain settings. This clears any settings from the previous domain.
applyDomainSettings(nestedScrollWebView, "", true, false);
import androidx.fragment.app.FragmentManager;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.views.NestedScrollWebView;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
// Assert that the intent is not null to remove the lint error below.
assert allowScreenshotsRestartIntent != null;
- // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. TODO.
+ // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`.
allowScreenshotsRestartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- // Make it so.
- startActivity(allowScreenshotsRestartIntent);
+ // Create a handler to restart the activity.
+ Handler allowScreenshotsRestartHandler = new Handler();
+
+ // Create a runnable to restart the activity.
+ Runnable allowScreenshotsRestartRunnable = () -> {
+ // Restart the activity.
+ startActivity(allowScreenshotsRestartIntent);
+
+ // Kill this instance of Privacy Browser. Otherwise, the app exhibits sporadic behavior after the restart.
+ System.exit(0);
+ };
+
+ // Restart the activity after 100 milliseconds, so that the app has enough time to save the change to the preference.
+ allowScreenshotsRestartHandler.postDelayed(allowScreenshotsRestartRunnable, 100);
break;
case "easylist":
// Assert that the intent is not null to remove the lint error below.
assert changeThemeRestartIntent != null;
- // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. TODO.
+ // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`.
changeThemeRestartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- // Make it so.
- startActivity(changeThemeRestartIntent);
+ // Create a handler to restart the activity.
+ Handler changeThemeRestartHandler = new Handler();
+
+ // Create a runnable to restart the activity.
+ Runnable changeThemeRestartRunnable = () -> {
+ // Restart the activity.
+ startActivity(changeThemeRestartIntent);
+
+ // Kill this instance of Privacy Browser. Otherwise, the app exhibits sporadic behavior after the restart.
+ System.exit(0);
+ };
+
+ // Restart the activity after 100 milliseconds, so that the app has enought time to save the change to the preference.
+ changeThemeRestartHandler.postDelayed(changeThemeRestartRunnable, 100);
break;
case "night_mode":
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp"
android:layout_margin="10dp"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end" />
</LinearLayout>
\ No newline at end of file
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp"
android:ellipsize="end"
- android:maxLines="1" />
+ android:lines="1" />
</LinearLayout>
<!-- Second row. -->
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp"
android:ellipsize="end"
- android:maxLines="1" />
+ android:lines="1" />
<!-- Third row. -->
<LinearLayout
android:textSize="22sp"
android:textStyle="italic"
android:ellipsize="end"
- android:maxLines="1" />
+ android:lines="1" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp"
android:layout_margin="10dp"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end" />
</LinearLayout>
\ No newline at end of file
<!-- A `CheckedTextView` allows the color of the text to be changed when it is selected (checked). -->
<CheckedTextView
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="20dp"
android:paddingEnd="20dp"
tools:ignore="contentDescription" />
<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp"
android:layout_margin="10dp"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end" />
</LinearLayout>
\ No newline at end of file
android:id="@+id/spinner_dropdown_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_margin="10dp"
- android:maxLines="1"
+ android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="22sp" />
</LinearLayout>
\ No newline at end of file
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="20dp"
android:paddingEnd="20dp"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:layout_width="wrap_content"
android:layout_margin="10dp"
android:textSize="16sp"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end" />
</LinearLayout>
\ No newline at end of file
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:id="@+id/spinner_item_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="?attr/urlHistoryText"
android:textSize="16sp"
android:layout_margin="10dp"
- android:maxLines="1"
+ android:lines="1"
android:ellipsize="end"/>
</LinearLayout>
\ No newline at end of file
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ android:background="?attr/webViewBackground" >
+ <!-- The WebView is initially `visibility="invisible"` so that the background color matches the theme. -->
<com.stoutner.privacybrowser.views.NestedScrollWebView
android:id="@+id/nestedscroll_webview"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:focusable="true"
- android:focusableInTouchMode="true" />
+ android:focusableInTouchMode="true"
+ android:visibility="invisible" />
<!-- `android:max` changes the maximum `ProgressBar` value from 10000 to 100 to match progress percentage.
`android:layout_height="2dp"` works best for API >= 23, but `3dp` is required for visibility on API <= 22.
<!-- `android:windowTranslucentStatus` makes the system status bar translucent. When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
<style name="PrivacyBrowserLight" parent="Theme.AppCompat.Light.NoActionBar" >
<item name="aboutIcon">@drawable/about_light</item>
- <item name="android:windowTranslucentStatus">true</item>
<item name="addTabIconTintColor">@color/gray_700</item>
+ <item name="android:textColorHighlight">@color/blue_200</item>
<item name="android:textColorPrimary">@color/primary_text_color_selector_light</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
<item name="colorAccent">@color/blue_700</item>
<item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
- <item name="android:textColorHighlight">@color/blue_200</item>
<item name="mainStatusBarBackground">@color/gray_500</item>
<item name="navigationHeaderBackground">@color/blue_700</item>
<item name="navigationHeaderTextColor">@color/white</item>
- <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
<item name="navigationIconTintColor">@color/blue_800</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
<item name="progressTintColor">@color/blue_700</item>
<item name="sslTitle">@color/blue_900</item>
<item name="urlHistoryText">@color/black</item>
<item name="viewSourceIconTintColor">@color/black</item>
+ <item name="webViewBackground">@color/gray_50</item>
</style>
<!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. -->
<!-- `android:windowTranslucentStatus` makes the system status bar translucent. When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
<style name="PrivacyBrowserDark" parent="Theme.AppCompat.NoActionBar" >
<item name="aboutIcon">@drawable/about_dark</item>
- <item name="android:windowTranslucentStatus">true</item>
<item name="addTabIconTintColor">@color/gray_400</item>
+ <item name="android:textColorHighlight">@color/blue_800</item>
<item name="android:textColorPrimary">@color/primary_text_color_selector_dark</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
<item name="colorAccent">@color/blue_600</item>
- <item name="android:textColorHighlight">@color/blue_800</item>
<item name="mainStatusBarBackground">@color/black</item>
<item name="navigationHeaderBackground">@color/blue_800</item>
<item name="navigationHeaderTextColor">@color/gray_300</item>
- <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
<item name="findOnPageIconTintColor">@color/blue_600</item>
<item name="navigationIconTintColor">@color/blue_600</item>
<item name="progressTintColor">@color/blue_600</item>
<item name="sslTitle">@color/blue_700</item>
<item name="urlHistoryText">@color/gray_200</item>
<item name="viewSourceIconTintColor">@color/gray_300</item>
+ <item name="webViewBackground">@color/gray_900</item>
</style>
<!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->
<attr name="spinnerBackground" format="reference" />
<attr name="spinnerTextColorSelector" format="reference" />
<attr name="urlHistoryText" format="reference" />
+ <attr name="webViewBackground" format="reference" />
<attr name="navigationHeaderBackground" format="reference" />
<attr name="navigationHeaderTextColor" format="reference" />
<color name="dark_green_15">#FF163317</color>
+ <color name="gray_50">#FFFAFAFA</color>
<color name="gray_100">#FFF5F5F5</color>
<color name="gray_200">#FFEEEEEE</color>
<color name="gray_300">#FFE0E0E0</color>
<!-- `android:windowTranslucentStatus` makes the system status bar translucent. When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
<style name="PrivacyBrowserLight" parent="Theme.AppCompat.Light.NoActionBar" >
<item name="aboutIcon">@drawable/about_light</item>
- <item name="android:windowTranslucentStatus">true</item>
<item name="addTabIconTintColor">@color/gray_700</item>
+ <item name="android:textColorHighlight">@color/blue_200</item>
<item name="android:textColorPrimary">@color/primary_text_color_selector_light</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
<item name="colorAccent">@color/blue_700</item>
<item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
- <item name="android:textColorHighlight">@color/blue_200</item>
<item name="mainStatusBarBackground">@color/gray_500</item>
<item name="navigationHeaderBackground">@color/blue_700</item>
<item name="navigationHeaderTextColor">@color/white</item>
- <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
<item name="navigationIconTintColor">@color/blue_800</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
<item name="progressTintColor">@color/blue_700</item>
<item name="sslTitle">@color/blue_900</item>
<item name="urlHistoryText">@color/black</item>
<item name="viewSourceIconTintColor">@color/black</item>
+ <item name="webViewBackground">@color/gray_50</item>
</style>
<!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. -->
<!-- `android:windowTranslucentStatus` makes the system status bar translucent. When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
<style name="PrivacyBrowserDark" parent="Theme.AppCompat.NoActionBar" >
<item name="aboutIcon">@drawable/about_dark</item>
- <item name="android:windowTranslucentStatus">true</item>
<item name="addTabIconTintColor">@color/gray_400</item>
+ <item name="android:textColorHighlight">@color/blue_800</item>
<item name="android:textColorPrimary">@color/primary_text_color_selector_dark</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
<item name="colorAccent">@color/blue_600</item>
- <item name="android:textColorHighlight">@color/blue_800</item>
<item name="mainStatusBarBackground">@color/black</item>
<item name="navigationHeaderBackground">@color/blue_800</item>
<item name="navigationHeaderTextColor">@color/gray_300</item>
- <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
<item name="findOnPageIconTintColor">@color/blue_600</item>
<item name="navigationIconTintColor">@color/blue_600</item>
<item name="progressTintColor">@color/blue_600</item>
<item name="sslTitle">@color/blue_700</item>
<item name="urlHistoryText">@color/gray_200</item>
<item name="viewSourceIconTintColor">@color/gray_300</item>
+ <item name="webViewBackground">@color/gray_900</item>
</style>
<!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->