import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.PreferenceManager;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
+import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebViewDatabase;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.stoutner.privacybrowser.R;
import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
import com.stoutner.privacybrowser.dialogs.DownloadImageDialog;
+import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog;
import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.OrbotProxyHelper;
import com.stoutner.privacybrowser.dialogs.DownloadFileDialog;
// We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener,
- PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener {
+ HttpAuthenticationDialog.HttpAuthenticationListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener,
+ DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener {
// `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`,
- // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`,
- // `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+ // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`,
+ // `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
public static boolean darkTheme;
// `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
// `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings()` and `applyDomainSettings()`.
public static boolean displayWebpageImagesBoolean;
- // `reloadOnRestartBoolean` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
- public static boolean reloadOnRestartBoolean;
+ // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
+ public static boolean reloadOnRestart;
+
+ // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`.
+ public static boolean loadUrlOnRestart;
// The pinned domain SSL Certificate variables are public static so they can be accessed from `PinnedSslCertificateMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`.
public static int domainSettingsDatabaseId;
- public static boolean pinnedDomainSslCertificate;
public static String pinnedDomainSslIssuedToCNameString;
public static String pinnedDomainSslIssuedToONameString;
public static String pinnedDomainSslIssuedToUNameString;
// `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`.
private final Map<String, String> customHeaders = new HashMap<>();
- // `javaScriptEnabled` is also used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `applyAppSettings()`.
- // It is `Boolean` instead of `boolean` because `applyAppSettings()` needs to know if it is `null`.
- private Boolean javaScriptEnabled;
+ // `javaScriptEnabled` is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+ private boolean javaScriptEnabled;
- // `firstPartyCookiesEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyAppSettings()`.
+ // `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`.
private boolean firstPartyCookiesEnabled;
- // `thirdPartyCookiesEnabled` used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
+ // `thirdPartyCookiesEnabled` used in `onCreate()`, `onPrepareOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
private boolean thirdPartyCookiesEnabled;
- // `domStorageEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
+ // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
private boolean domStorageEnabled;
- // `saveFormDataEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
+ // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
private boolean saveFormDataEnabled;
+ // `nightMode` is used in `onCreate()` and `applyDomainSettings()`.
+ private boolean nightMode;
+
// `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`.
private boolean swipeToRefreshEnabled;
// `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
private SslErrorHandler sslErrorHandler;
+ // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`.
+ private static HttpAuthHandler httpAuthHandler;
+
// `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`.
private InputMethodManager inputMethodManager;
// `urlIsLoading` is used in `onCreate()`, `loadUrl()`, and `applyDomainSettings()`.
private boolean urlIsLoading;
+ // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`.
+ private boolean pinnedDomainSslCertificate;
+
+ // `bookmarksDatabaseHelper` is used in `onCreate()` and `loadBookmarksFolder()`.
+ private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+
+ // `bookmarksListView` is used in `onCreate()` and `loadBookmarksFolder()`.
+ private ListView bookmarksListView;
+
+ // `currentBookmarksFolder` is used in `onCreate()`, `onBackPressed()`, and `loadBookmarksFolder()`.
+ private String currentBookmarksFolder;
+
+ // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`.
+ private TextView bookmarksTitleTextView;
@Override
// Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers.
// Get a handle for `inputMethodManager`.
inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21.
+ // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
supportAppBar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(supportAppBar);
appBar = getSupportActionBar();
// Get handles for views that need to be accessed.
drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
rootCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.root_coordinatorlayout);
+ bookmarksListView = (ListView) findViewById(R.id.bookmarks_drawer_listview);
+ bookmarksTitleTextView = (TextView) findViewById(R.id.bookmarks_title_textview);
+ FloatingActionButton createBookmarksFolderFab = (FloatingActionButton) findViewById(R.id.create_bookmark_folder_fab);
+ FloatingActionButton createBookmarkFab = (FloatingActionButton) findViewById(R.id.create_bookmark_fab);
mainWebViewRelativeLayout = (RelativeLayout) findViewById(R.id.main_webview_relativelayout);
mainWebView = (WebView) findViewById(R.id.main_webview);
findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout);
urlAppBarRelativeLayout = (RelativeLayout) findViewById(R.id.url_app_bar_relativelayout);
favoriteIconImageView = (ImageView) findViewById(R.id.favorite_icon);
+ // Set the bookmarks drawer resources according to the theme. This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
+ if (darkTheme) {
+ createBookmarksFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_dark));
+ createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_dark));
+ bookmarksListView.setBackgroundColor(getResources().getColor(R.color.gray_850));
+ } else {
+ createBookmarksFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_light));
+ createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_light));
+ bookmarksListView.setBackgroundColor(getResources().getColor(R.color.white));
+ }
+
// Create a double-tap listener to toggle full-screen mode.
final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
// Override `onDoubleTap()`. All other events are handled using the default settings.
}
});
- // `DrawerTitle` identifies the `DrawerLayout` in accessibility mode.
+ // `DrawerTitle` identifies the `DrawerLayouts` in accessibility mode.
drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
+ drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
// Listen for touches on the navigation menu.
final NavigationView navigationView = (NavigationView) findViewById(R.id.navigationview);
final MenuItem navigationForwardMenuItem = navigationMenu.getItem(2);
final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3);
+ // Initialize the bookmarks database helper. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`.
+ // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
+ bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0);
+
+ // Initialize `currentBookmarksFolder`. `""` is the home folder in the database.
+ currentBookmarksFolder = "";
+
+ // Load the home folder, which is `""` in the database.
+ loadBookmarksFolder();
+
+ bookmarksListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // Convert the id from long to int to match the format of the bookmarks database.
+ int databaseID = (int) id;
+
+ // Get the bookmark `Cursor` for this ID and move it to the first row.
+ Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID);
+ bookmarkCursor.moveToFirst();
+
+ // Act upon the bookmark according to the type.
+ if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { // The selected bookmark is a folder.
+ // Store the new folder name in `currentBookmarksFolder`.
+ currentBookmarksFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+
+ // Load the new folder.
+ loadBookmarksFolder();
+ } else { // The selected bookmark is not a folder.
+ // Load the bookmark URL.
+ loadUrl(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)));
+
+ // Close the bookmarks drawer.
+ drawerLayout.closeDrawer(GravityCompat.END);
+ }
+
+ // Close the `Cursor`.
+ bookmarkCursor.close();
+ }
+ });
+
// The `DrawerListener` allows us to update the Navigation Menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
}
}
+ // Handle HTTP authentication requests.
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
+ // Store `handler` so it can be accessed from `onHttpAuthenticationCancel()` and `onHttpAuthenticationProceed()`.
+ httpAuthHandler = handler;
+
+ // Display the HTTP authentication dialog.
+ AppCompatDialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm);
+ httpAuthenticationDialogFragment.show(getSupportFragmentManager(), getString(R.string.http_authentication));
+ }
+
// Update the URL in urlTextBox when the page starts to load.
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
+ if (nightMode) {
+ mainWebView.setVisibility(View.INVISIBLE);
+ }
+
+ // Hide the keyboard. `0` indicates no additional flags.
+ inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
+
// Check to see if we are waiting on Orbot.
if (!waitingForOrbot) { // We are not waiting on Orbot, so we need to process the URL.
// We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
}
}
- // Update formattedUrlString and urlTextBox. It is necessary to do this after the page finishes loading because the final URL can change during load.
+ // It is necessary to update `formattedUrlString` and `urlTextBox` after the page finishes loading because the final URL can change during load.
@Override
public void onPageFinished(WebView view, String url) {
// Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes.
!currentWebsiteSslStartDateString.equals(pinnedDomainSslStartDateString) || !currentWebsiteSslEndDateString.equals(pinnedDomainSslEndDateString)) { // The pinned SSL certificate doesn't match the current domain certificate.
//Display the pinned SSL certificate mismatch `AlertDialog`.
AppCompatDialogFragment pinnedSslCertificateMismatchDialogFragment = new PinnedSslCertificateMismatchDialog();
- pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.ssl_certificate_mismatch));
+ pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_mismatch));
}
}
}
// Display the SSL error `AlertDialog`.
AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error);
- sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.ssl_certificate_error));
+ sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error));
}
}
});
// Update the progress bar when a page is loading.
@Override
public void onProgressChanged(WebView view, int progress) {
+ // Inject the night mode CSS if night mode is enabled.
+ if (nightMode) {
+ // `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. `!important` takes precedent over any existing sub-settings.
+ mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = '" +
+ "* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important; text-shadow: none !important; border: none !important;}" +
+ "a {color: #1565C0 !important;}" +
+ "'; parent.appendChild(style)})()", new ValueCallback<String>() {
+ @Override
+ public void onReceiveValue(String value) {
+ // Initialize a `Handler` to display `mainWebView`.
+ Handler displayWebViewHandler = new Handler();
+
+ // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied.
+ Runnable displayWebViewRunnable = new Runnable() {
+ public void run() {
+ // Only display `mainWebView` if the progress bar is one. This prevents the display of the `WebView` while it is still loading.
+ if (progressBar.getVisibility() == View.GONE) {
+ mainWebView.setVisibility(View.VISIBLE);
+ }
+ }
+ };
+
+ // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
+ displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
+ }
+ });
+ }
+
progressBar.setProgress(progress);
if (progress < 100) {
+ // Show the progress bar.
progressBar.setVisibility(View.VISIBLE);
} else {
+ // Hide the progress bar.
progressBar.setVisibility(View.GONE);
+ // Display `mainWebView` 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 (!nightMode) {
+ mainWebView.setVisibility(View.VISIBLE);
+ }
+
//Stop the `SwipeToRefresh` indicator if it is running
swipeRefreshLayout.setRefreshing(false);
}
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
// Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
- downloadFileDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+ downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
}
});
// Set `mainWebView` to load in overview mode (zoomed out to the maximum width).
mainWebView.getSettings().setLoadWithOverviewMode(true);
+ // Explicitly disable geolocation.
+ mainWebView.getSettings().setGeolocationEnabled(false);
+
// Initialize cookieManager.
cookieManager = CookieManager.getInstance();
thirdPartyCookiesEnabled = false;
domStorageEnabled = false;
saveFormDataEnabled = false;
+ nightMode = false;
// Initialize `webViewTitle`.
webViewTitle = getString(R.string.no_title);
setDisplayWebpageImages();
// Reload the webpage if displaying of images has been disabled in `SettingsFragment`.
- if (reloadOnRestartBoolean) {
+ if (reloadOnRestart) {
// Reload `mainWebView`.
mainWebView.reload();
// Reset `reloadOnRestartBoolean`.
- reloadOnRestartBoolean = false;
+ reloadOnRestart = false;
+ }
+
+ // Load the URL on restart to apply changes to night mode.
+ if (loadUrlOnRestart) {
+ // Load the current `formattedUrlString`.
+ loadUrl(formattedUrlString);
+
+ // Reset `loadUrlOnRestart.
+ loadUrlOnRestart = false;
}
}
// Prepare the font size title and current size menu item.
switch (fontSize) {
case 25:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.twenty_five_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.twenty_five_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_twenty_five_percent);
break;
case 50:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.fifty_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.fifty_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_fifty_percent);
break;
case 75:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.seventy_five_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.seventy_five_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_seventy_five_percent);
break;
case 100:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
break;
case 125:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_twenty_five_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_twenty_five_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_twenty_five_percent);
break;
case 150:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_fifty_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_fifty_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_fifty_percent);
break;
case 175:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_seventy_five_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_seventy_five_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_seventy_five_percent);
break;
case 200:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.two_hundred_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.two_hundred_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_two_hundred_percent);
break;
default:
- fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+ fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
break;
}
PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
// Print the document. The print attributes are `null`.
- printManager.print(getResources().getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+ printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
- createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.create_shortcut));
+ createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
//Everything else will be handled by `CreateHomeScreenShortcutDialog` and the associated listener below.
return true;
// Show the `UrlHistoryDialog` `AlertDialog` and name this instance `R.string.history`. `this` is the `Context`.
AppCompatDialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList);
- urlHistoryDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.history));
+ urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
break;
case R.id.bookmarks:
@Override
public boolean onMenuItemClick(MenuItem item) {
// Save the link URL in a `ClipData`.
- ClipData srcAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), linkUrl);
+ ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
// Set the `ClipData` as the clipboard's primary clip.
clipboardManager.setPrimaryClip(srcAnchorTypeClipData);
@Override
public boolean onMenuItemClick(MenuItem item) {
// Save the email address in a `ClipData`.
- ClipData srcEmailTypeClipData = ClipData.newPlainText(getResources().getString(R.string.email_address), linkUrl);
+ ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl);
// Set the `ClipData` as the clipboard's primary clip.
clipboardManager.setPrimaryClip(srcEmailTypeClipData);
public boolean onMenuItemClick(MenuItem item) {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
- downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+ downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
return false;
}
});
@Override
public boolean onMenuItemClick(MenuItem item) {
// Save the image URL in a `ClipData`.
- ClipData srcImageTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl);
+ ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
// Set the `ClipData` as the clipboard's primary clip.
clipboardManager.setPrimaryClip(srcImageTypeClipData);
public boolean onMenuItemClick(MenuItem item) {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
- downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+ downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
return false;
}
});
@Override
public boolean onMenuItemClick(MenuItem item) {
// Save the image URL in a `ClipData`.
- ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl);
+ ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
// Set the `ClipData` as the clipboard's primary clip.
clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
}
}
+ @Override
+ public void onHttpAuthenticationCancel() {
+ // Cancel the `HttpAuthHandler`.
+ httpAuthHandler.cancel();
+ }
+
+ @Override
+ public void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment) {
+ // Get handles for the `EditTexts`.
+ EditText usernameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_username);
+ EditText passwordEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_password);
+
+ // Proceed with the HTTP authentication.
+ httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
+ }
+
public void viewSslCertificate(View view) {
// Show the `ViewSslCertificateDialog` `AlertDialog` and name this instance `@string/view_ssl_certificate`.
DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog();
- viewSslCertificateDialogFragment.show(getFragmentManager(), getResources().getString(R.string.view_ssl_certificate));
+ viewSslCertificateDialogFragment.show(getFragmentManager(), getString(R.string.view_ssl_certificate));
}
@Override
// Override `onBackPressed` to handle the navigation drawer and `mainWebView`.
@Override
public void onBackPressed() {
- // Close the navigation drawer if it is available. GravityCompat.START is the drawer on the left on Left-to-Right layout text.
- if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+ if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open.
+ // Close the navigation drawer.
drawerLayout.closeDrawer(GravityCompat.START);
- } else {
- // Load the previous URL if available.
- if (mainWebView.canGoBack()) {
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
-
- // Go back.
- mainWebView.goBack();
- } else {
- // Pass `onBackPressed()` to the system.
- super.onBackPressed();
+ } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open.
+ if (currentBookmarksFolder.isEmpty()) { // The home folder is displayed.
+ // close the bookmarks drawer.
+ drawerLayout.closeDrawer(GravityCompat.END);
+ } else { // A subfolder is displayed.
+ // Place the former parent folder in `currentFolder`.
+ currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolder(currentBookmarksFolder);
+
+ // Load the new folder.
+ loadBookmarksFolder();
}
+
+ } else if (mainWebView.canGoBack()) { // There is at least one item in the `WebView` history.
+ // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
+ navigatingHistory = true;
+
+ // Go back.
+ mainWebView.goBack();
+ } else { // There isn't anything to do in Privacy Browser.
+ // Pass `onBackPressed()` to the system.
+ super.onBackPressed();
}
}
}
loadUrl(formattedUrlString);
-
- // Hide the keyboard so we can see the webpage. `0` indicates no additional flags.
- inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
}
// Get a handle for the shared preference. `this` references the current context.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Store the default font size and user agent information.
+ // Store the general preference information.
String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
String defaultUserAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0");
String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
+ nightMode = sharedPreferences.getBoolean("night_mode", false);
if (domainSettingsApplied) { // The url we are loading has custom domain settings.
// Get a cursor for the current host and move it to the first position.
String userAgentString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
+ int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
pinnedDomainSslIssuedByONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
pinnedDomainSslIssuedByUNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
+ // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used.
+ switch (nightModeInt) {
+ case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
+ nightMode = true;
+ break;
+
+ case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
+ nightMode = false;
+ break;
+ }
+
+ // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
+ if (nightMode) {
+ javaScriptEnabled = true;
+ }
+
// Set the pinned SSL certificate start date to `null` if the saved date `long` is 0.
if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
pinnedDomainSslStartDate = null;
domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false);
saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);
+ // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
+ if (nightMode) {
+ javaScriptEnabled = true;
+ }
+
// Apply the default settings.
mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
}
+
+ private void loadBookmarksFolder() {
+ // Update `bookmarksCursor` with the contents of the bookmarks database for the current folder.
+ Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentBookmarksFolder);
+
+ // Setup a `CursorAdapter`. `this` specifies the `Context`. `false` disables `autoRequery`.
+ CursorAdapter bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ // Inflate the individual item layout. `false` does not attach it to the root.
+ return getLayoutInflater().inflate(R.layout.bookmarks_drawer_item_linearlayout, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Get handles for the views.
+ ImageView bookmarkFavoriteIcon = (ImageView) view.findViewById(R.id.bookmark_favorite_icon);
+ TextView bookmarkNameTextView = (TextView) view.findViewById(R.id.bookmark_name);
+
+ // Get the favorite icon byte array from the `Cursor`.
+ byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
+
+ // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
+ Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
+
+ // Display the bitmap in `bookmarkFavoriteIcon`.
+ bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap);
+
+ // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`.
+ String bookmarkNameString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+ bookmarkNameTextView.setText(bookmarkNameString);
+
+ // Make the font bold for folders.
+ if (cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {
+ bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD);
+ } else { // Reset the font to default for normal bookmarks.
+ bookmarkNameTextView.setTypeface(Typeface.DEFAULT);
+ }
+ }
+ };
+
+ // Populate the `ListView` with the adapter.
+ bookmarksListView.setAdapter(bookmarksCursorAdapter);
+
+ // Set the bookmarks drawer title.
+ if (currentBookmarksFolder.isEmpty()) {
+ bookmarksTitleTextView.setText(R.string.bookmarks);
+ } else {
+ bookmarksTitleTextView.setText(currentBookmarksFolder);
+ }
+ }
}