// Include the following AndroidX libraries.
implementation 'androidx.arch.core:core-common:2.1.0'
implementation 'androidx.arch.core:core-runtime:2.1.0'
- implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
- implementation "androidx.core:core-ktx:1.3.0"
- implementation 'androidx.drawerlayout:drawerlayout:1.0.0'
+ implementation "androidx.core:core-ktx:1.3.1"
+ implementation 'androidx.drawerlayout:drawerlayout:1.1.0'
implementation 'androidx.preference:preference:1.1.1'
- implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
+ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'androidx.webkit:webkit:1.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
// Include the Google material library.
- implementation 'com.google.android.material:material:1.1.0'
+ implementation 'com.google.android.material:material:1.2.0'
// Only compile Firebase ads for the free flavor.
- freeImplementation 'com.google.firebase:firebase-ads:19.1.0'
+ freeImplementation 'com.google.firebase:firebase-ads:19.3.0'
}
\ No newline at end of file
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar; // The AndroidX toolbar must be used until the minimum API is >= 21.
+import androidx.appcompat.widget.Toolbar;
import androidx.core.app.NavUtils;
import androidx.fragment.app.DialogFragment;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
public class BookmarksActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener {
public static boolean restartFromBookmarksDatabaseViewActivity;
+ // Define the saved instance state constants.
+ private final String CHECKED_BOOKMARKS_ARRAY_LIST = "checked_bookmarks_array_list";
+
+ // Define the class menu items.
+ private MenuItem moveBookmarkUpMenuItem;
+ private MenuItem moveBookmarkDownMenuItem;
+
// `bookmarksDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`,
// `onMoveToFolder()`, `deleteBookmarkFolderContents()`, `loadFolder()`, and `onDestroy()`.
private BookmarksDatabaseHelper bookmarksDatabaseHelper;
// `oldFolderName` is used in `onCreate()` and `onSaveBookmarkFolder()`.
private String oldFolderNameString;
- // `moveBookmarkUpMenuItem` is used in `onCreate()` and `updateMoveIcons()`.
- private MenuItem moveBookmarkUpMenuItem;
-
- // `moveBookmarkDownMenuItem` is used in `onCreate()` and `updateMoveIcons()`.
- private MenuItem moveBookmarkDownMenuItem;
-
// `bookmarksDeletedSnackbar` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`.
private Snackbar bookmarksDeletedSnackbar;
// Display the create bookmark dialog.
createBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_bookmark));
});
+
+ // Restore the state if the app has been restarted.
+ if (savedInstanceState != null) {
+ // Update the bookmarks list view after it has loaded.
+ bookmarksListView.post(() -> {
+ // Get the checked bookmarks array list.
+ ArrayList<Integer> checkedBookmarksArrayList = savedInstanceState.getIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST);
+
+ // Check each previously checked bookmark in the list view. When the minimum API >= 24 a `forEach()` command can be used instead.
+ if (checkedBookmarksArrayList != null) {
+ for (int i = 0; i < checkedBookmarksArrayList.size(); i++) {
+ bookmarksListView.setItemChecked(checkedBookmarksArrayList.get(i), true);
+ }
+ }
+ });
+ }
}
@Override
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Get the array of the checked items.
+ SparseBooleanArray checkedBookmarksSparseBooleanArray = bookmarksListView.getCheckedItemPositions();
+
+ // Create a checked items array list.
+ ArrayList<Integer> checkedBookmarksArrayList = new ArrayList<>();
+
+ // Add each checked bookmark position to the array list.
+ for (int i = 0; i < checkedBookmarksSparseBooleanArray.size(); i++) {
+ // Check to see if the bookmark is currently checked. Bookmarks that have previously been checked but currently aren't will be populated in the sparse boolean array, but will return false.
+ if (checkedBookmarksSparseBooleanArray.valueAt(i)) {
+ // Add the bookmark position to the checked bookmarks array list.
+ checkedBookmarksArrayList.add(checkedBookmarksSparseBooleanArray.keyAt(i));
+ }
+ }
+
+ // Store the checked items array list in the saved instance state.
+ savedInstanceState.putIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST, checkedBookmarksArrayList);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu.
public class BookmarksDatabaseViewActivity extends AppCompatActivity implements EditBookmarkDatabaseViewDialog.EditBookmarkDatabaseViewListener,
EditBookmarkFolderDatabaseViewDialog.EditBookmarkFolderDatabaseViewListener {
- // Instantiate the constants.
+ // Define the class constants.
private static final int ALL_FOLDERS_DATABASE_ID = -2;
public static final int HOME_FOLDER_DATABASE_ID = -1;
- // `bookmarksDatabaseHelper` is used in `onCreate()`, `updateBookmarksListView()`, `selectAllBookmarksInFolder()`, and `onDestroy()`.
- private BookmarksDatabaseHelper bookmarksDatabaseHelper;
-
- // `bookmarksCursor` is used in `onCreate()`, `updateBookmarksListView()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, and `onDestroy()`.
- private Cursor bookmarksCursor;
-
- // `bookmarksCursorAdapter` is used in `onCreate()`, `selectAllBookmarksInFolder()`, and `updateBookmarksListView()`.
- private CursorAdapter bookmarksCursorAdapter;
+ // Define the saved instance state constants.
+ private final String CURRENT_FOLDER_DATABASE_ID = "current_folder_database_id";
+ private final String CURRENT_FOLDER_NAME = "current_folder_name";
+ private final String SORT_BY_DISPLAY_ORDER = "sort_by_display_order";
- // `oldFolderNameString` is used in `onCreate()` and `onSaveBookmarkFolder()`.
- private String oldFolderNameString;
-
- // `currentFolderDatabaseId` is used in `onCreate()`, `updateBookmarksListView()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`.
+ // Define the class variables.
private int currentFolderDatabaseId;
-
- // `currentFolder` is used in `onCreate()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`.
private String currentFolderName;
-
- // `sortByDisplayOrder` is used in `onCreate()`, `onOptionsItemSelected()`, and `updateBookmarksListView()`.
private boolean sortByDisplayOrder;
-
- // `bookmarksDeletedSnackbar` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`.
+ private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+ private Cursor bookmarksCursor;
+ private CursorAdapter bookmarksCursorAdapter;
+ private String oldFolderNameString;
private Snackbar bookmarksDeletedSnackbar;
-
- // `closeActivityAfterDismissingSnackbar` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`.
private boolean closeActivityAfterDismissingSnackbar;
@Override
// Set the content view.
setContentView(R.layout.bookmarks_databaseview_coordinatorlayout);
- // The AndroidX toolbar must be used until the minimum API is >= 21.
+ // Get a handle for the toolbar.
Toolbar toolbar = findViewById(R.id.bookmarks_databaseview_toolbar);
+
+ // Set the support action bar.
setSupportActionBar(toolbar);
// Get a handle for the action bar.
// Get a cursor with the list of all the folders.
Cursor foldersCursor = bookmarksDatabaseHelper.getAllFolders();
- // Combine `matrixCursor` and `foldersCursor`.
+ // Combine the matrix cursor and the folders cursor.
MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
// Get the default folder bitmap. `ContextCompat` must be used until the minimum API >= 21.
Drawable defaultFolderDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.folder_blue_bitmap);
- // Cast the default folder drawable to a `BitmapDrawable`.
+ // Cast the default folder drawable to a bitmap drawable.
BitmapDrawable defaultFolderBitmapDrawable = (BitmapDrawable) defaultFolderDrawable;
// Remove the incorrect lint warning that `.getBitmap()` might be null.
assert defaultFolderBitmapDrawable != null;
- // Convert the default folder `BitmapDrawable` to a bitmap.
+ // Convert the default folder bitmap drawable to a bitmap.
Bitmap defaultFolderBitmap = defaultFolderBitmapDrawable.getBitmap();
Spinner folderSpinner = findViewById(R.id.spinner);
folderSpinner.setAdapter(foldersCursorAdapter);
- // Handle taps on the spinner dropdown.
- folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- // Store the current folder database ID.
- currentFolderDatabaseId = (int) id;
+ // Wait to set the on item selected listener until the spinner has been inflated. Otherwise the activity will crash on restart.
+ folderSpinner.post(() -> {
+ // Handle taps on the spinner dropdown.
+ folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ // Store the current folder database ID.
+ currentFolderDatabaseId = (int) id;
- // Get a handle for the selected view.
- TextView selectedFolderTextView = findViewById(R.id.spinner_item_textview);
+ // Get a handle for the selected view.
+ TextView selectedFolderTextView = findViewById(R.id.spinner_item_textview);
- // Store the current folder name.
- currentFolderName = selectedFolderTextView.getText().toString();
+ // Store the current folder name.
+ currentFolderName = selectedFolderTextView.getText().toString();
- // Update the list view.
- updateBookmarksListView();
- }
+ // Update the list view.
+ updateBookmarksListView();
+ }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Do nothing.
- }
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing.
+ }
+ });
});
- // Get a handle for the bookmarks `ListView`.
+
+ // Get a handle for the bookmarks listview.
ListView bookmarksListView = findViewById(R.id.bookmarks_databaseview_listview);
- // Get a `Cursor` with the current contents of the bookmarks database.
- bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarks();
+ // Check to see if the activity was restarted.
+ if (savedInstanceState == null) { // The activity was not restarted.
+ // Set the default current folder database ID.
+ currentFolderDatabaseId = ALL_FOLDERS_DATABASE_ID;
+ } else { // The activity was restarted.
+ // Restore the class variables from the saved instance state.
+ currentFolderDatabaseId = savedInstanceState.getInt(CURRENT_FOLDER_DATABASE_ID);
+ currentFolderName = savedInstanceState.getString(CURRENT_FOLDER_NAME);
+ sortByDisplayOrder = savedInstanceState.getBoolean(SORT_BY_DISPLAY_ORDER);
+
+ // Update the spinner if the home folder is selected. Android handles this by default for the main cursor but not the matrix cursor.
+ if (currentFolderDatabaseId == HOME_FOLDER_DATABASE_ID) {
+ folderSpinner.setSelection(1);
+ }
+ }
+
+ // Update the bookmarks listview.
+ updateBookmarksListView();
// Setup a `CursorAdapter` with `this` context. `false` disables autoRequery.
bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
// Update the ListView.
bookmarksListView.setAdapter(bookmarksCursorAdapter);
- // Set the current folder database ID.
- currentFolderDatabaseId = ALL_FOLDERS_DATABASE_ID;
-
// Set a listener to edit a bookmark when it is tapped.
bookmarksListView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
// Convert the database ID to an int.
// Disable the delete menu item if a delete is pending.
deleteMenuItem.setEnabled(!deletingBookmarks);
+ // Get the number of currently selected bookmarks.
+ int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount();
+
+ // Set the action mode subtitle according to the number of selected bookmarks. This must be set here or it will be missing if the activity is restarted.
+ mode.setSubtitle(getString(R.string.selected) + " " + numberOfSelectedBookmarks);
+
+ // Do not show the select all menu item if all the bookmarks are already checked.
+ if (bookmarksListView.getCheckedItemCount() == bookmarksListView.getCount()) {
+ selectAllMenuItem.setVisible(false);
+ }
+
// Make it so.
return true;
}
// Calculate the number of selected bookmarks.
int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount();
- // Adjust the ActionMode according to the number of selected bookmarks.
+ // Update the action mode subtitle according to the number of selected bookmarks.
mode.setSubtitle(getString(R.string.selected) + " " + numberOfSelectedBookmarks);
- // Do not show the select all menu item if all the bookmarks are already checked.
- if (bookmarksListView.getCheckedItemCount() == bookmarksListView.getCount()) {
+ // Update the visibility of the the select all menu.
+ if (bookmarksListView.getCheckedItemCount() == bookmarksListView.getCount()) { // All of the bookmarks are checked.
+ // Hide the select all menu item.
selectAllMenuItem.setVisible(false);
- } else {
+ } else { // Not all of the bookmarks are checked.
+ // Show the select all menu item.
selectAllMenuItem.setVisible(true);
}
// Inflate the menu.
getMenuInflater().inflate(R.menu.bookmarks_databaseview_options_menu, menu);
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Get a handle for the sort menu item.
+ MenuItem sortMenuItem = menu.findItem(R.id.sort);
+
+ // Change the sort menu item icon if the listview is sorted by display order, which restores the state after a restart.
+ if (sortByDisplayOrder) {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ sortMenuItem.setIcon(R.drawable.sort_selected_day);
+ } else {
+ sortMenuItem.setIcon(R.drawable.sort_selected_night);
+ }
+ }
+
// Success.
return true;
}
onBackPressed();
break;
- case R.id.options_menu_sort:
+ case R.id.sort:
// Update the sort by display order tracker.
sortByDisplayOrder = !sortByDisplayOrder;
// Update the icon and display a snackbar.
if (sortByDisplayOrder) { // Sort by display order.
// Update the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- menuItem.setIcon(R.drawable.sort_selected_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
menuItem.setIcon(R.drawable.sort_selected_day);
+ } else {
+ menuItem.setIcon(R.drawable.sort_selected_night);
}
// Display a Snackbar indicating the current sort type.
Snackbar.make(bookmarksListView, R.string.sorted_by_display_order, Snackbar.LENGTH_SHORT).show();
} else { // Sort by database id.
// Update the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- menuItem.setIcon(R.drawable.sort_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
menuItem.setIcon(R.drawable.sort_day);
+ } else {
+ menuItem.setIcon(R.drawable.sort_night);
}
// Display a Snackbar indicating the current sort type.
return true;
}
+ @Override
+ public void onSaveInstanceState (@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Store the class variables in the bundle.
+ savedInstanceState.putInt(CURRENT_FOLDER_DATABASE_ID, currentFolderDatabaseId);
+ savedInstanceState.putString(CURRENT_FOLDER_NAME, currentFolderName);
+ savedInstanceState.putBoolean(SORT_BY_DISPLAY_ORDER, sortByDisplayOrder);
+ }
+
@Override
public void onBackPressed() {
// Check to see if a snackbar is currently displayed. If so, it must be closed before existing so that a pending delete is completed before reloading the list view in the bookmarks activity.
}
}
- // Update the list view.
- bookmarksCursorAdapter.changeCursor(bookmarksCursor);
+ // Update the cursor adapter if it isn't null, which happens when the activity is restarted.
+ if (bookmarksCursorAdapter != null) {
+ bookmarksCursorAdapter.changeCursor(bookmarksCursor);
+ }
}
private void selectAllBookmarksInFolder(int folderId) {
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
+import android.widget.ScrollView;
import android.widget.Spinner;
-import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar; // The AndroidX toolbar must be used until the minimum API is >= 21.
+import androidx.appcompat.widget.SwitchCompat;
+import androidx.appcompat.widget.Toolbar;
import androidx.core.app.NavUtils;
import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentManager; // The AndroidX dialog fragment must be used or an error is produced on API <=22.
+import androidx.fragment.app.FragmentManager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.Objects;
public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, DomainsListFragment.DismissSnackbarInterface {
- // `twoPanedMode` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `populateDomainsListView()`.
+ // Define the public static variables.
+ public static int domainsListViewPosition;
public static boolean twoPanedMode;
-
- // `databaseId` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreateOptionsMenu()`, `saveDomainSettings()` and `populateDomainsListView()`.
public static int currentDomainDatabaseId;
-
- // `deleteMenuItem` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`.
public static MenuItem deleteMenuItem;
-
- // `dismissingSnackbar` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onOptionsItemSelected()`.
public static boolean dismissingSnackbar;
// The SSL certificate and IP address information are accessed from `DomainSettingsFragment` and `saveDomainSettings()`.
public static String currentIpAddresses;
+ // Initialize the class constants.
+ private final String LISTVIEW_POSITION = "listview_position";
+ private final String DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed";
+ private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_is";
+ private final String DOMAIN_SETTINGS_SCROLL_Y = "domain_settings_scroll_y";
+
+ // Initialize the class variables.
+ private boolean restartAfterRotate;
+ private boolean domainSettingsDisplayedBeforeRotate;
+ private int domainSettingsDatabaseIdBeforeRotate;
+ private int domainSettingsScrollY = 0;
+
+ // Defile the class views.
+ private ListView domainsListView;
+
// `closeActivityAfterDismissingSnackbar` is used in `onOptionsItemSelected()`, and `onBackPressed()`.
private boolean closeActivityAfterDismissingSnackbar;
// `domainsDatabaseHelper` is used in `onCreate()`, `saveDomainSettings()`, and `onDestroy()`.
private static DomainsDatabaseHelper domainsDatabaseHelper;
- // `domainsListView` is used in `onCreate()` and `populateDomainsList()`.
- private ListView domainsListView;
-
// `addDomainFAB` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`.
private FloatingActionButton addDomainFAB;
// `deletedDomainPosition` is used in an inner and outer class in `onOptionsItemSelected()`.
private int deletedDomainPosition;
- // `restartAfterRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private boolean restartAfterRotate;
-
- // `domainSettingsDisplayedBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private boolean domainSettingsDisplayedBeforeRotate;
-
- // `domainSettingsDatabaseIdBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private int domainSettingsDatabaseIdBeforeRotate;
-
// `goDirectlyToDatabaseId` is used in `onCreate()` and `onCreateOptionsMenu()`.
private int goDirectlyToDatabaseId;
// Run the default commands.
super.onCreate(savedInstanceState);
- // Extract the values from `savedInstanceState` if it is not `null`.
+ // Initialize the domains listview position.
+ domainsListViewPosition = 0;
+
+ // Extract the values from the saved instance state if it is not null.
if (savedInstanceState != null) {
+ domainsListViewPosition = savedInstanceState.getInt(LISTVIEW_POSITION);
restartAfterRotate = true;
- domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean("domain_settings_displayed");
- domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt("domain_settings_database_id");
+ domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean(DOMAIN_SETTINGS_DISPLAYED);
+ domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt(DOMAIN_SETTINGS_DATABASE_ID);
+ domainSettingsScrollY = savedInstanceState.getInt(DOMAIN_SETTINGS_SCROLL_Y);
}
// Get the launching intent
fragmentManager.executePendingTransactions();
// Populate the list of domains. `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation.
- populateDomainsListView(domainSettingsDatabaseIdBeforeRotate);
+ populateDomainsListView(domainSettingsDatabaseIdBeforeRotate, domainsListViewPosition);
} else { // The device is in single-paned mode.
// Reset `restartAfterRotate`.
restartAfterRotate = false;
// Store the current domain database ID.
currentDomainDatabaseId = domainSettingsDatabaseIdBeforeRotate;
- // Add `currentDomainDatabaseId` to `argumentsBundle`.
+ // Create an arguments bundle.
Bundle argumentsBundle = new Bundle();
+
+ // Add the domain settings arguments.
argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+ argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
- // Add `argumentsBundle` to `domainSettingsFragment`.
+ // Instantiate a new domain settings fragment.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+ // Add the arguments bundle to the domain settings fragment.
domainSettingsFragment.setArguments(argumentsBundle);
- // Show `deleteMenuItem`.
+ // Show the delete menu item.
deleteMenuItem.setVisible(true);
// Hide the add domain floating action button.
addDomainFAB.hide();
- // Display `domainSettingsFragment`.
+ // Display the domain settings fragment.
fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
}
} else { // The device was not rotated or, if it was, domain settings were not displayed previously.
fragmentManager.executePendingTransactions();
// Populate the list of domains. `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation.
- populateDomainsListView(goDirectlyToDatabaseId);
+ populateDomainsListView(goDirectlyToDatabaseId, domainsListViewPosition);
} else { // The device is in single-paned mode.
- // Add the domain ID to be loaded to `argumentsBundle`.
+ // Create an arguments bundle.
Bundle argumentsBundle = new Bundle();
+
+ // Add the domain settings to arguments bundle.
argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, goDirectlyToDatabaseId);
+ argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
- // Add `argumentsBundle` to `domainSettingsFragment`.
+ // Instantiate a new domain settings fragment.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+ // Add the arguments bundle to the domain settings fragment`.
domainSettingsFragment.setArguments(argumentsBundle);
- // Show `deleteMenuItem`.
+ // Show the delete menu item.
deleteMenuItem.setVisible(true);
// Hide the add domain floating action button.
addDomainFAB.hide();
- // Display `domainSettingsFragment`.
+ // Display the domain settings fragment.
fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
}
} else { // Highlight the first domain.
fragmentManager.executePendingTransactions();
// Populate the list of domains. `-1` highlights the first domain.
- populateDomainsListView(-1);
+ populateDomainsListView(-1, domainsListViewPosition);
}
}
fragmentManager.executePendingTransactions();
// Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode.
- populateDomainsListView(-1);
+ populateDomainsListView(-1, domainsListViewPosition);
// Show the add domain floating action button.
addDomainFAB.show();
public void onDismissed(Snackbar snackbar, int event) {
// Run commands based on the event.
if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) { // The user pushed the `Undo` button.
- // Store the database ID in arguments bundle.
+ // Create an arguments bundle.
Bundle argumentsBundle = new Bundle();
+
+ // Store the domains settings in the arguments bundle.
argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete);
+ argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
- // Add the arguments bundle to the domain settings fragment.
+ // Instantiate a new domain settings fragment.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+ // Add the arguments bundle to the domain settings fragment.
domainSettingsFragment.setArguments(argumentsBundle);
// Display the correct fragments.
deleteMenuItem.setIcon(R.drawable.delete_day);
}
} else { // The device in in one-paned mode.
- // Display `domainSettingsFragment`.
+ // Display the domain settings fragment.
fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
// Hide the add domain floating action button.
addDomainFAB.hide();
- // Show and enable `deleteMenuItem`.
+ // Show and enable the delete menu item.
deleteMenuItem.setVisible(true);
- // Display `domainSettingsFragment`.
+ // Display the domain settings fragment.
fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
}
} else { // The snackbar was dismissed without the undo button being pushed.
}
@Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- // Store the current `DomainSettingsFragment` state in `outState`.
- if (findViewById(R.id.domain_settings_scrollview) != null) { // `DomainSettingsFragment` is displayed.
+ protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Get a handle for the domain settings scrollview.
+ ScrollView domainSettingsScrollView = findViewById(R.id.domain_settings_scrollview);
+
+ // Check to see if the domain settings scrollview exists.
+ if (domainSettingsScrollView == null) { // The domain settings are not displayed.
+ // Store the domain settings status in the bundle.
+ savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, false);
+ savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, -1);
+ savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, 0);
+ } else { // The domain settings are displayed.
// Save any changes that have been made to the domain settings.
saveDomainSettings(coordinatorLayout, resources);
- // Store `DomainSettingsDisplayed`.
- outState.putBoolean("domain_settings_displayed", true);
- outState.putInt("domain_settings_database_id", DomainSettingsFragment.databaseId);
- } else { // `DomainSettingsFragment` is not displayed.
- outState.putBoolean("domain_settings_displayed", false);
- outState.putInt("domain_settings_database_id", -1);
+ // Get the domain settings scroll Y.
+ int domainSettingsScrollY = domainSettingsScrollView.getScrollY();
+
+ // Store the domain settings status in the bundle.
+ savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, true);
+ savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, DomainSettingsFragment.databaseId);
+ savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, domainSettingsScrollY);
}
- super.onSaveInstanceState(outState);
+ // Check to see if the domains listview exists.
+ if (domainsListView != null) {
+ // Get the domains listview position.
+ int domainsListViewPosition = domainsListView.getFirstVisiblePosition();
+
+ // Store the listview position in the bundle.
+ savedInstanceState.putInt(LISTVIEW_POSITION, domainsListViewPosition);
+ }
}
// Control what the navigation bar back button does.
fragmentManager.executePendingTransactions();
// Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode.
- populateDomainsListView(-1);
+ populateDomainsListView(-1, domainsListViewPosition);
// Show the add domain floating action button.
addDomainFAB.show();
// Display the newly created domain.
if (twoPanedMode) { // The device in in two-paned mode.
- populateDomainsListView(currentDomainDatabaseId);
+ populateDomainsListView(currentDomainDatabaseId, 0);
} else { // The device is in single-paned mode.
// Hide the add domain floating action button.
addDomainFAB.hide();
- // Show and enable `deleteMenuItem`.
+ // Show and enable the delete menu item.
DomainsActivity.deleteMenuItem.setVisible(true);
- // Add the current domain database ID to the arguments bundle.
+ // Create an arguments bundle.
Bundle argumentsBundle = new Bundle();
+
+ // Add the domain settings to the arguments bundle. The scroll Y should always be `0` on a new domain.
argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+ argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, 0);
- // Add and arguments bundle to the domain setting fragment.
+ // Instantiate a new domain settings fragment.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+ // Add the arguments bundle to the domain setting fragment.
domainSettingsFragment.setArguments(argumentsBundle);
// Display the domain settings fragment.
public void saveDomainSettings(View view, Resources resources) {
// Get handles for the domain settings.
EditText domainNameEditText = view.findViewById(R.id.domain_settings_name_edittext);
- Switch javaScriptSwitch = view.findViewById(R.id.javascript_switch);
- Switch firstPartyCookiesSwitch = view.findViewById(R.id.first_party_cookies_switch);
- Switch thirdPartyCookiesSwitch = view.findViewById(R.id.third_party_cookies_switch);
- Switch domStorageSwitch = view.findViewById(R.id.dom_storage_switch);
- Switch formDataSwitch = view.findViewById(R.id.form_data_switch); // Form data can be removed once the minimum API >= 26.
- Switch easyListSwitch = view.findViewById(R.id.easylist_switch);
- Switch easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch);
- Switch fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch);
- Switch fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch);
- Switch ultraListSwitch = view.findViewById(R.id.ultralist_switch);
- Switch ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch);
- Switch blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch);
+ SwitchCompat javaScriptSwitch = view.findViewById(R.id.javascript_switch);
+ SwitchCompat firstPartyCookiesSwitch = view.findViewById(R.id.first_party_cookies_switch);
+ SwitchCompat thirdPartyCookiesSwitch = view.findViewById(R.id.third_party_cookies_switch);
+ SwitchCompat domStorageSwitch = view.findViewById(R.id.dom_storage_switch);
+ SwitchCompat formDataSwitch = view.findViewById(R.id.form_data_switch); // Form data can be removed once the minimum API >= 26.
+ SwitchCompat easyListSwitch = view.findViewById(R.id.easylist_switch);
+ SwitchCompat easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch);
+ SwitchCompat fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch);
+ SwitchCompat fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch);
+ SwitchCompat ultraListSwitch = view.findViewById(R.id.ultralist_switch);
+ SwitchCompat ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch);
+ SwitchCompat blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch);
Spinner userAgentSpinner = view.findViewById(R.id.user_agent_spinner);
EditText customUserAgentEditText = view.findViewById(R.id.custom_user_agent_edittext);
Spinner fontSizeSpinner = view.findViewById(R.id.font_size_spinner);
Spinner webViewThemeSpinner = view.findViewById(R.id.webview_theme_spinner);
Spinner wideViewportSpinner = view.findViewById(R.id.wide_viewport_spinner);
Spinner displayWebpageImagesSpinner = view.findViewById(R.id.display_webpage_images_spinner);
- Switch pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch);
+ SwitchCompat pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch);
RadioButton currentWebsiteCertificateRadioButton = view.findViewById(R.id.current_website_certificate_radiobutton);
- Switch pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch);
+ SwitchCompat pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch);
RadioButton currentIpAddressesRadioButton = view.findViewById(R.id.current_ip_addresses_radiobutton);
// Extract the data for the domain settings.
}
}
- private void populateDomainsListView(final int highlightedDomainDatabaseId) {
+ private void populateDomainsListView(final int highlightedDomainDatabaseId, int domainsListViewPosition) {
// get a handle for the current `domains_listview`.
domainsListView = findViewById(R.id.domains_listview);
// Update the list view.
domainsListView.setAdapter(domainsCursorAdapter);
+ // Restore the scroll position.
+ domainsListView.setSelection(domainsListViewPosition);
+
// Display the domain settings in the second pane if operating in two pane mode and the database contains at least one domain.
if (DomainsActivity.twoPanedMode && (domainsCursor.getCount() > 0)) { // Two-paned mode is enabled and there is at least one domain.
// Initialize `highlightedDomainPosition`.
domainsCursor.moveToPosition(highlightedDomainPosition);
currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
- // Store the database ID in the arguments bundle.
+ // Create an arguments bundle.
Bundle argumentsBundle = new Bundle();
+
+ // Store the domain settings in the arguments bundle.
argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+ argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
- // Add and arguments bundle to the domain settings fragment.
+ // Instantiate a new domain settings fragment.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+ // Add the arguments bundle to the domain settings fragment.
domainSettingsFragment.setArguments(argumentsBundle);
// Display the domain settings fragment.
import javax.crypto.spec.SecretKeySpec;
public class ImportExportActivity extends AppCompatActivity implements StoragePermissionDialog.StoragePermissionDialogListener {
- // Create the encryption constants.
+ // Define the encryption constants.
private final int NO_ENCRYPTION = 0;
private final int PASSWORD_ENCRYPTION = 1;
private final int OPENPGP_ENCRYPTION = 2;
- // Create the activity result constants.
+ // Define the activity result constants.
private final int BROWSE_RESULT_CODE = 0;
private final int OPENPGP_EXPORT_RESULT_CODE = 1;
- // `openKeychainInstalled` is accessed from an inner class.
+ // Define the saved instance state constants.
+ private final String PASSWORD_ENCRYPTED_TEXTINPUTLAYOUT_VISIBILITY = "password_encrypted_textinputlayout_visibility";
+ private final String KITKAT_PASSWORD_ENCRYPTED_TEXTVIEW_VISIBILITY = "kitkat_password_encrypted_textview_visibility";
+ private final String OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY = "open_keychain_required_textview_visibility";
+ private final String FILE_LOCATION_CARD_VIEW = "file_location_card_view";
+ private final String FILE_NAME_LINEARLAYOUT_VISIBILITY = "file_name_linearlayout_visibility";
+ private final String FILE_DOES_NOT_EXIST_TEXTVIEW_VISIBILITY = "file_does_not_exist_textview_visibility";
+ private final String FILE_EXISTS_WARNING_TEXTVIEW_VISIBILITY = "file_exists_warning_textview_visibility";
+ private final String OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY = "open_keychain_import_instructions_textview_visibility";
+ private final String IMPORT_EXPORT_BUTTON_VISIBILITY = "import_export_button_visibility";
+ private final String FILE_NAME_TEXT = "file_name_text";
+ private final String IMPORT_EXPORT_BUTTON_TEXT = "import_export_button_text";
+
+ // Define the class views.
+ TextInputLayout passwordEncryptionTextInputLayout;
+ TextView kitKatPasswordEncryptionTextView;
+ TextView openKeychainRequiredTextView;
+ CardView fileLocationCardView;
+ LinearLayout fileNameLinearLayout;
+ EditText fileNameEditText;
+ TextView fileDoesNotExistTextView;
+ TextView fileExistsWarningTextView;
+ TextView openKeychainImportInstructionsTextView;
+ Button importExportButton;
+
+ // Define the class variables.
private boolean openKeychainInstalled;
@Override
// Get handles for the views that need to be modified.
Spinner encryptionSpinner = findViewById(R.id.encryption_spinner);
- TextInputLayout passwordEncryptionTextInputLayout = findViewById(R.id.password_encryption_textinputlayout);
+ passwordEncryptionTextInputLayout = findViewById(R.id.password_encryption_textinputlayout);
EditText encryptionPasswordEditText = findViewById(R.id.password_encryption_edittext);
- TextView kitKatPasswordEncryptionTextView = findViewById(R.id.kitkat_password_encryption_textview);
- TextView openKeychainRequiredTextView = findViewById(R.id.openkeychain_required_textview);
- CardView fileLocationCardView = findViewById(R.id.file_location_cardview);
+ kitKatPasswordEncryptionTextView = findViewById(R.id.kitkat_password_encryption_textview);
+ openKeychainRequiredTextView = findViewById(R.id.openkeychain_required_textview);
+ fileLocationCardView = findViewById(R.id.file_location_cardview);
RadioButton importRadioButton = findViewById(R.id.import_radiobutton);
RadioButton exportRadioButton = findViewById(R.id.export_radiobutton);
- LinearLayout fileNameLinearLayout = findViewById(R.id.file_name_linearlayout);
- EditText fileNameEditText = findViewById(R.id.file_name_edittext);
- TextView fileDoesNotExistTextView = findViewById(R.id.file_does_not_exist_textview);
- TextView fileExistsWarningTextView = findViewById(R.id.file_exists_warning_textview);
- TextView openKeychainImportInstructionsTextView = findViewById(R.id.openkeychain_import_instructions_textview);
- Button importExportButton = findViewById(R.id.import_export_button);
+ fileNameLinearLayout = findViewById(R.id.file_name_linearlayout);
+ fileNameEditText = findViewById(R.id.file_name_edittext);
+ fileDoesNotExistTextView = findViewById(R.id.file_does_not_exist_textview);
+ fileExistsWarningTextView = findViewById(R.id.file_exists_warning_textview);
+ openKeychainImportInstructionsTextView = findViewById(R.id.openkeychain_import_instructions_textview);
+ importExportButton = findViewById(R.id.import_export_button);
TextView storagePermissionTextView = findViewById(R.id.import_export_storage_permission_textview);
// Create an array adapter for the spinner.
// Set the array adapter for the spinner.
encryptionSpinner.setAdapter(encryptionArrayAdapter);
- // Initially hide the unneeded views.
- passwordEncryptionTextInputLayout.setVisibility(View.GONE);
- kitKatPasswordEncryptionTextView.setVisibility(View.GONE);
- openKeychainRequiredTextView.setVisibility(View.GONE);
- fileNameLinearLayout.setVisibility(View.GONE);
- fileDoesNotExistTextView.setVisibility(View.GONE);
- fileExistsWarningTextView.setVisibility(View.GONE);
- openKeychainImportInstructionsTextView.setVisibility(View.GONE);
- importExportButton.setVisibility(View.GONE);
-
// Instantiate the download location helper.
DownloadLocationHelper downloadLocationHelper = new DownloadLocationHelper();
}
}
});
+
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState == null) { // The app has not been restarted.
+ // Initially hide the unneeded views.
+ passwordEncryptionTextInputLayout.setVisibility(View.GONE);
+ kitKatPasswordEncryptionTextView.setVisibility(View.GONE);
+ openKeychainRequiredTextView.setVisibility(View.GONE);
+ fileNameLinearLayout.setVisibility(View.GONE);
+ fileDoesNotExistTextView.setVisibility(View.GONE);
+ fileExistsWarningTextView.setVisibility(View.GONE);
+ openKeychainImportInstructionsTextView.setVisibility(View.GONE);
+ importExportButton.setVisibility(View.GONE);
+ } else { // The app has been restarted.
+ // Restore the visibility of the views.
+ passwordEncryptionTextInputLayout.setVisibility(savedInstanceState.getInt(PASSWORD_ENCRYPTED_TEXTINPUTLAYOUT_VISIBILITY));
+ kitKatPasswordEncryptionTextView.setVisibility(savedInstanceState.getInt(KITKAT_PASSWORD_ENCRYPTED_TEXTVIEW_VISIBILITY));
+ openKeychainRequiredTextView.setVisibility(savedInstanceState.getInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY));
+ fileLocationCardView.setVisibility(savedInstanceState.getInt(FILE_LOCATION_CARD_VIEW));
+ fileNameLinearLayout.setVisibility(savedInstanceState.getInt(FILE_NAME_LINEARLAYOUT_VISIBILITY));
+ fileDoesNotExistTextView.setVisibility(savedInstanceState.getInt(FILE_DOES_NOT_EXIST_TEXTVIEW_VISIBILITY));
+ fileExistsWarningTextView.setVisibility(savedInstanceState.getInt(FILE_EXISTS_WARNING_TEXTVIEW_VISIBILITY));
+ openKeychainImportInstructionsTextView.setVisibility(savedInstanceState.getInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY));
+ importExportButton.setVisibility(savedInstanceState.getInt(IMPORT_EXPORT_BUTTON_VISIBILITY));
+
+ // Restore the text.
+ fileNameEditText.post(() -> fileNameEditText.setText(savedInstanceState.getString(FILE_NAME_TEXT)));
+ importExportButton.setText(savedInstanceState.getString(IMPORT_EXPORT_BUTTON_TEXT));
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState (@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Save the visibility of the views.
+ savedInstanceState.putInt(PASSWORD_ENCRYPTED_TEXTINPUTLAYOUT_VISIBILITY, passwordEncryptionTextInputLayout.getVisibility());
+ savedInstanceState.putInt(KITKAT_PASSWORD_ENCRYPTED_TEXTVIEW_VISIBILITY, kitKatPasswordEncryptionTextView.getVisibility());
+ savedInstanceState.putInt(OPEN_KEYCHAIN_REQUIRED_TEXTVIEW_VISIBILITY, openKeychainRequiredTextView.getVisibility());
+ savedInstanceState.putInt(FILE_LOCATION_CARD_VIEW, fileLocationCardView.getVisibility());
+ savedInstanceState.putInt(FILE_NAME_LINEARLAYOUT_VISIBILITY, fileNameLinearLayout.getVisibility());
+ savedInstanceState.putInt(FILE_DOES_NOT_EXIST_TEXTVIEW_VISIBILITY, fileDoesNotExistTextView.getVisibility());
+ savedInstanceState.putInt(FILE_EXISTS_WARNING_TEXTVIEW_VISIBILITY, fileExistsWarningTextView.getVisibility());
+ savedInstanceState.putInt(OPEN_KEYCHAIN_IMPORT_INSTRUCTIONS_TEXTVIEW_VISIBILITY, openKeychainImportInstructionsTextView.getVisibility());
+ savedInstanceState.putInt(IMPORT_EXPORT_BUTTON_VISIBILITY, importExportButton.getVisibility());
+
+ // Save the text.
+ savedInstanceState.putString(FILE_NAME_TEXT, fileNameEditText.getText().toString());
+ savedInstanceState.putString(IMPORT_EXPORT_BUTTON_TEXT, importExportButton.getText().toString());
}
public void onClickRadioButton(View view) {
import android.content.res.Configuration;
import android.media.MediaScannerConnection;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar;
import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.asynctasks.GetLogcat;
import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
import com.stoutner.privacybrowser.dialogs.SaveLogcatDialog;
import com.stoutner.privacybrowser.helpers.FileNameHelper;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialog.SaveLogcatListener, StoragePermissionDialog.StoragePermissionDialogListener {
+ // Initialize the saved instance state constants.
+ private final String SCROLLVIEW_POSITION = "scrollview_position";
+
+ // Define the class variables.
private String filePathString;
+ // Define the class views.
+ private TextView logcatTextView;
+
@Override
public void onCreate(Bundle savedInstanceState) {
// Get a handle for the shared preferences.
// Display the the back arrow in the action bar.
actionBar.setDisplayHomeAsUpEnabled(true);
+ // Populate the class views.
+ logcatTextView = findViewById(R.id.logcat_textview);
+
// Implement swipe to refresh.
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.logcat_swiperefreshlayout);
swipeRefreshLayout.setOnRefreshListener(() -> {
// Get the current logcat.
- new GetLogcat(this).execute();
+ new GetLogcat(this, 0).execute();
});
// Get the current theme status.
// Set the swipe refresh background color.
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt);
+ // Initialize the scrollview Y position int.
+ int scrollViewYPositionInt = 0;
+
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState != null) {
+ // Get the saved scrollview position.
+ scrollViewYPositionInt = savedInstanceState.getInt(SCROLLVIEW_POSITION);
+ }
+
// Get the logcat.
- new GetLogcat(this).execute();
+ new GetLogcat(this, scrollViewYPositionInt).execute();
}
@Override
// Get a handle for the clipboard manager.
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- // Get a handle for the logcat text view.
- TextView logcatTextView = findViewById(R.id.logcat_textview);
-
// Save the logcat in a ClipData.
ClipData logcatClipData = ClipData.newPlainText(getString(R.string.logcat), logcatTextView.getText());
process.waitFor();
// Reload the logcat.
- new GetLogcat(this).execute();
+ new GetLogcat(this, 0).execute();
} catch (IOException|InterruptedException exception) {
// Do nothing.
}
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Get a handle for the logcat scrollview.
+ ScrollView logcatScrollView = findViewById(R.id.logcat_scrollview);
+
+ // Get the scrollview Y position.
+ int scrollViewYPositionInt = logcatScrollView.getScrollY();
+
+ // Store the scrollview Y position in the bundle.
+ savedInstanceState.putInt(SCROLLVIEW_POSITION, scrollViewYPositionInt);
+ }
+
@Override
public void onSaveLogcat(DialogFragment dialogFragment) {
// Get a handle for the dialog fragment.
// Save the logcat.
saveLogcat(filePathString);
} else { // The storage permission was not granted.
- // Get a handle for the logcat text view.
- TextView logcatTextView = findViewById(R.id.logcat_textview);
-
// Display an error snackbar.
Snackbar.make(logcatTextView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
}
private void saveLogcat(String fileNameString) {
- // Get a handle for the logcat text view.
- TextView logcatTextView = findViewById(R.id.logcat_textview);
-
try {
// Get the logcat as a string.
String logcatString = logcatTextView.getText().toString();
}
}
}
-
- // `Void` does not declare any parameters. `Void` does not declare progress units. `String` contains the results.
- private static class GetLogcat extends AsyncTask<Void, Void, String> {
- // Create a weak reference to the calling activity.
- private final WeakReference<Activity> activityWeakReference;
-
- // Populate the weak reference to the calling activity.
- GetLogcat(Activity activity) {
- activityWeakReference = new WeakReference<>(activity);
- }
-
- @Override
- protected String doInBackground(Void... parameters) {
- // Get a handle for the activity.
- Activity activity = activityWeakReference.get();
-
- // Abort if the activity is gone.
- if ((activity == null) || activity.isFinishing()) {
- return "";
- }
-
- // Create a log string builder.
- StringBuilder logStringBuilder = new StringBuilder();
-
- try {
- // Get the logcat. `-b all` gets all the buffers (instead of just crash, main, and system). `-v long` produces more complete information. `-d` dumps the logcat and exits.
- Process process = Runtime.getRuntime().exec("logcat -b all -v long -d");
-
- // Wrap the logcat in a buffered reader.
- BufferedReader logBufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
-
- // Create a log transfer string.
- String logTransferString;
-
- // Use the log transfer string to copy the logcat from the buffered reader to the string builder.
- while ((logTransferString = logBufferedReader.readLine()) != null) {
- // Append a line.
- logStringBuilder.append(logTransferString);
-
- // Append a line break.
- logStringBuilder.append("\n");
- }
-
- // Close the buffered reader.
- logBufferedReader.close();
- } catch (IOException exception) {
- // Do nothing.
- }
-
- // Return the logcat.
- return logStringBuilder.toString();
- }
-
- // `onPostExecute()` operates on the UI thread.
- @Override
- protected void onPostExecute(String logcatString) {
- // Get a handle for the activity.
- Activity activity = activityWeakReference.get();
-
- // Abort if the activity is gone.
- if ((activity == null) || activity.isFinishing()) {
- return;
- }
-
- // Get handles for the views.
- TextView logcatTextView = activity.findViewById(R.id.logcat_textview);
- SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.logcat_swiperefreshlayout);
-
- // Display the logcat.
- logcatTextView.setText(logcatString);
-
- // Stop the swipe to refresh animation if it is displayed.
- swipeRefreshLayout.setRefreshing(false);
- }
- }
}
\ No newline at end of file
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
+import androidx.core.content.res.ResourcesCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
+ // The executor service handles background tasks. It is accessed from `ViewSourceActivity`. TODO. Change the number of threads, or create a single thread executor.
+ public static ExecutorService executorService = Executors.newFixedThreadPool(4);
+
// `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`.
public static String orbotStatus = "unknown";
private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2;
private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3;
+ // Define the saved instance state constants.
+ private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
+ private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
+ private final String SAVED_TAB_POSITION = "saved_tab_position";
+ private final String PROXY_MODE = "proxy_mode";
+
+ // Define the saved instance state variables.
+ private ArrayList<Bundle> savedStateArrayList;
+ private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
+ private int savedTabPosition;
+ private String savedProxyMode;
+
+ // Define the class views.
+ private AppBarLayout appBarLayout;
+ private TabLayout tabLayout;
+ private ViewPager webViewPager;
+
+ // Define the class variables.
+ private String newIntentUrl;
+
// The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
// `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
private NestedScrollWebView currentWebView;
// Run the default commands.
super.onCreate(savedInstanceState);
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState != null) {
+ // Store the saved instance state variables.
+ savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
+ savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
+ savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
+ savedProxyMode = savedInstanceState.getString(PROXY_MODE);
+ }
+
// Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default.
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
// Set the content view.
setContentView(R.layout.main_framelayout);
- // Get handles for the views that need to be modified.
+ // Get handles for the views.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ appBarLayout = findViewById(R.id.appbar_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
+ tabLayout = findViewById(R.id.tablayout);
+ webViewPager = findViewById(R.id.webviewpager);
// Get a handle for the app compat delegate.
AppCompatDelegate appCompatDelegate = getDelegate();
// Store up to 100 tabs in memory.
webViewPager.setOffscreenPageLimit(100);
+ // Initialize the app.
+ initializeApp();
+
+ // Apply the app settings from the shared preferences.
+ applyAppSettings();
+
// Populate the blocklists.
new PopulateBlocklists(this, this).execute();
}
url = intentUriData.toString();
}
- // Add a new tab if specified in the preferences.
- if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
- // Set the loading new intent flag.
- loadingNewIntent = true;
-
- // Add a new tab.
- addNewTab(url, true);
- } else { // Load the URL in the current tab.
- // Make it so.
- loadUrl(currentWebView, url);
+ // Check to see if the app is in the process of restarting
+ if (savedStateArrayList == null) { // The app is not in the process of being restarted. Process the new intent.
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
+ // Set the loading new intent flag.
+ loadingNewIntent = true;
+
+ // Add a new tab.
+ addNewTab(url, true);
+ } else { // Load the URL in the current tab.
+ // Make it so.
+ loadUrl(currentWebView, url);
+ }
+ } else { // The app is being restarted. Store the URL, which will be processed in `finishedPopulatingBlocklists()`.
+ newIntentUrl = url;
}
// Get a handle for the drawer layout.
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Create the saved state array lists.
+ ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
+ ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
+
+ // Get the URLs from each tab.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Create saved state bundle.
+ Bundle savedStateBundle = new Bundle();
+
+ // Get the current states.
+ nestedScrollWebView.saveState(savedStateBundle);
+ Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
+
+ // Store the saved states in the array lists.
+ savedStateArrayList.add(savedStateBundle);
+ savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
+ }
+ }
+
+ // Get the current tab position.
+ int currentTabPosition = tabLayout.getSelectedTabPosition();
+
+ // Store the saved states in the bundle.
+ savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
+ savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
+ savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
+ savedInstanceState.putString(PROXY_MODE, proxyMode);
+ }
+
@Override
public void onDestroy() {
// Unregister the orbot status broadcast receiver if it exists.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the current theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- // Set the dark stop icon.
- refreshMenuItem.setIcon(R.drawable.close_night);
- } else {
- // Set the light stop icon.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.close_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_night);
}
}
}
// Display a `Snackbar`.
if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
} else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
} else { // Privacy mode.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (cookieManager.acceptCookie()) { // First-party cookies are enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
} else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
} else { // Privacy mode.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
} else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (currentWebView.getSettings().getDomStorageEnabled()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
} else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (currentWebView.getSettings().getSaveFormData()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
} else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
}
// Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
return true;
case R.id.clear_cookies:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
return true;
case R.id.clear_dom_storage:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
// Form data can be remove once the minimum API >= 26.
case R.id.clear_form_data:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
// Override `onBackPressed` to handle the navigation drawer and and the WebViews.
@Override
public void onBackPressed() {
- // Get a handle for the drawer layout and the tab layout.
+ // Get a handle for the drawer layout.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open.
// Close the navigation drawer.
}
}
- private void applyAppSettings() {
- // Initialize the app if this is the first run. This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
- if (webViewDefaultUserAgent == null) {
- initializeApp();
- }
-
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
- // Store the values from the shared preferences in variables.
- incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
- boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
- sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
- sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
- sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
- proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
- fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
- hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
- scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
-
- // Get the search string.
- String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
-
- // Set the search string.
- if (searchString.equals("Custom URL")) { // A custom search string is used.
- searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
- } else { // A custom search string is not used.
- searchURL = searchString;
- }
-
- // Get a handle for the app compat delegate.
- AppCompatDelegate appCompatDelegate = getDelegate();
-
- // Get handles for the views that need to be modified.
- FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- ActionBar actionBar = appCompatDelegate.getSupportActionBar();
- Toolbar toolbar = findViewById(R.id.toolbar);
- LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
- LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
- // Remove the incorrect lint warning below that the action bar might be null.
- assert actionBar != null;
-
- // Apply the proxy.
- applyProxy(false);
-
- // Set Do Not Track status.
- if (doNotTrackEnabled) {
- customHeaders.put("DNT", "1");
- } else {
- customHeaders.remove("DNT");
- }
-
- // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
- CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
- AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
- AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
- AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
-
- // Add the scrolling behavior to the layout parameters.
- if (scrollAppBar) {
- // Enable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
- toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- } else {
- // Disable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(null);
- toolbarLayoutParams.setScrollFlags(0);
- findOnPageLayoutParams.setScrollFlags(0);
- tabsLayoutParams.setScrollFlags(0);
-
- // Expand the app bar if it is currently collapsed.
- appBarLayout.setExpanded(true);
- }
-
- // Apply the modified layout parameters.
- swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
- toolbar.setLayoutParams(toolbarLayoutParams);
- findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
- tabsLinearLayout.setLayoutParams(tabsLayoutParams);
-
- // Set the app bar scrolling for each WebView.
- for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Only modify the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Set the app bar scrolling.
- nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
- }
- }
-
- // Update the full screen browsing mode settings.
- if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
- // Update the visibility of the app bar, which might have changed in the settings.
- if (hideAppBar) {
- // Hide the tab linear layout.
- tabsLinearLayout.setVisibility(View.GONE);
-
- // Hide the action bar.
- actionBar.hide();
- } else {
- // Show the tab linear layout.
- tabsLinearLayout.setVisibility(View.VISIBLE);
-
- // Show the action bar.
- actionBar.show();
- }
-
- // Hide the banner ad in the free flavor.
- if (BuildConfig.FLAVOR.contentEquals("free")) {
- AdHelper.hideAd(findViewById(R.id.adview));
- }
-
- /* Hide the system bars.
- * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
- * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
- * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
- * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
- */
- rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- } else { // Privacy Browser is not in full screen browsing mode.
- // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
- inFullScreenBrowsingMode = false;
-
- // Show the tab linear layout.
- tabsLinearLayout.setVisibility(View.VISIBLE);
-
- // Show the action bar.
- actionBar.show();
-
- // Show the banner ad in the free flavor.
- if (BuildConfig.FLAVOR.contentEquals("free")) {
- // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead.
- AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
- }
-
- // Remove the `SYSTEM_UI` flags from the root frame layout.
- rootFrameLayout.setSystemUiVisibility(0);
- }
- }
-
private void initializeApp() {
// Get a handle for the input method.
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// Remove the lint warning below that the input method manager might be null.
assert inputMethodManager != null;
- // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23.
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the red color span according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ } else {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+ }
+
// Get handles for the URL views.
EditText urlEditText = findViewById(R.id.url_edittext);
// Get handles for views that need to be modified.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
NavigationView navigationView = findViewById(R.id.navigationview);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab);
FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
// Listen for touches on the navigation menu.
navigationView.setNavigationItemSelectedListener(this);
- // Get handles for the navigation menu and the back and forward menu items. The menu is 0 based.
+ // Get handles for the navigation menu and the back and forward menu items.
Menu navigationMenu = navigationView.getMenu();
- MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
- MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
- MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ MenuItem navigationBackMenuItem = navigationMenu.findItem(R.id.back);
+ MenuItem navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
+ MenuItem navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// Update the web view pager every time a tab is modified.
webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// Set the current WebView.
setCurrentWebView(position);
- // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
+ // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab.
if (tabLayout.getSelectedTabPosition() != position) {
// Create a handler to select the tab.
Handler selectTabHandler = new Handler();
tab.select();
};
- // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
+ // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated. TODO. Switch to a post command.
selectTabHandler.postDelayed(selectTabRunnable, 150);
}
}
defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset();
defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset();
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
// Set the refresh color scheme according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
}
// Initialize a color background typed value.
bareWebView.destroy();
}
+ private void applyAppSettings() {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Store the values from the shared preferences in variables.
+ incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+ boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+ sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+ sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+ sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+ proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
+ fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
+ hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
+ scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+
+ // Apply the saved proxy mode if the app has been restarted.
+ if (savedProxyMode != null) {
+ // Apply the saved proxy mode.
+ proxyMode = savedProxyMode;
+
+ // Reset the saved proxy mode.
+ savedProxyMode = null;
+ }
+
+ // Get the search string.
+ String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+ // Set the search string.
+ if (searchString.equals("Custom URL")) { // A custom search string is used.
+ searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+ } else { // A custom search string is not used.
+ searchURL = searchString;
+ }
+
+ // Get a handle for the app compat delegate.
+ AppCompatDelegate appCompatDelegate = getDelegate();
+
+ // Get handles for the views that need to be modified.
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+
+ // Remove the incorrect lint warning below that the action bar might be null.
+ assert actionBar != null;
+
+ // Apply the proxy.
+ applyProxy(false);
+
+ // Set Do Not Track status.
+ if (doNotTrackEnabled) {
+ customHeaders.put("DNT", "1");
+ } else {
+ customHeaders.remove("DNT");
+ }
+
+ // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+ AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+ AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+ AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+ // Add the scrolling behavior to the layout parameters.
+ if (scrollAppBar) {
+ // Enable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+ toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ } else {
+ // Disable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(null);
+ toolbarLayoutParams.setScrollFlags(0);
+ findOnPageLayoutParams.setScrollFlags(0);
+ tabsLayoutParams.setScrollFlags(0);
+
+ // Expand the app bar if it is currently collapsed.
+ appBarLayout.setExpanded(true);
+ }
+
+ // Apply the modified layout parameters.
+ swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+ toolbar.setLayoutParams(toolbarLayoutParams);
+ findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+ tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+ // Set the app bar scrolling for each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only modify the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Set the app bar scrolling.
+ nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+ }
+ }
+
+ // Update the full screen browsing mode settings.
+ if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
+ // Update the visibility of the app bar, which might have changed in the settings.
+ if (hideAppBar) {
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
+
+ // Hide the action bar.
+ actionBar.hide();
+ } else {
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
+ actionBar.show();
+ }
+
+ // Hide the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ AdHelper.hideAd(findViewById(R.id.adview));
+ }
+
+ /* Hide the system bars.
+ * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+ * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+ * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+ */
+ rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else { // Privacy Browser is not in full screen browsing mode.
+ // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
+ inFullScreenBrowsingMode = false;
+
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
+ actionBar.show();
+
+ // Show the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead.
+ AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ }
+
+ // Remove the `SYSTEM_UI` flags from the root frame layout.
+ rootFrameLayout.setSystemUiVisibility(0);
+ }
+ }
+
@Override
public void navigateHistory(String url, int steps) {
// Apply the domain settings.
// Get the current page position.
int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
-
// Get the corresponding tab.
TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
- // Create the pinned SSL date variables.
+ // Get the pinned SSL date longs.
+ long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE));
+ long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE));
+
+ // Define the pinned SSL date variables.
Date pinnedSslStartDate;
Date pinnedSslEndDate;
- // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
- if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+ // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslStartDateLong == 0) {
pinnedSslStartDate = null;
} else {
- pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+ pinnedSslStartDate = new Date(pinnedSslStartDateLong);
}
- // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
- if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+ // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslEndDateLong == 0) {
pinnedSslEndDate = null;
} else {
- pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+ pinnedSslEndDate = new Date(pinnedSslEndDateLong);
}
// Close the current host domain settings cursor.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
break;
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // Set a background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from the shared preferences.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
}
// Set the loading of webpage images.
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
- // Set a transparent background on URL edit text. The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21.
- urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ // Set a transparent background on URL edit text.
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
// Close the domains database helper.
}
private void applyProxy(boolean reloadWebViews) {
- // Get a handle for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// Set the proxy according to the mode. `this` refers to the current activity where an alert dialog might be displayed.
ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode);
case ProxyHelper.TOR:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if Orbot is installed.
case ProxyHelper.I2P:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if I2P is installed.
case ProxyHelper.CUSTOM:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
break;
}
if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled.
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
} else { // First-party cookies are disabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
+ } else {
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
}
}
if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) { // Both JavaScript and DOM storage are enabled.
domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
} else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled but DOM storage is disabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
}
} else { // JavaScript is disabled, so DOM storage is ghosted.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
}
}
// Update the refresh icon.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
// `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
ultraList = combinedBlocklists.get(4);
ultraPrivacy = combinedBlocklists.get(5);
- // Add the first tab.
- addNewTab("", true);
+ // Check to see if the activity has been restarted.
+ if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) { // The activity has not been restarted or it was restarted on start to force the night theme.
+ // Add the first tab.
+ addNewTab("", true);
+ } else { // The activity has been restarted.
+ // Restore each tab. Once the minimum API >= 24, a `forEach()` command can be used.
+ for (int i = 0; i < savedStateArrayList.size(); i++) {
+ // Add a new tab.
+ tabLayout.addTab(tabLayout.newTab());
+
+ // Get the new tab.
+ TabLayout.Tab newTab = tabLayout.getTabAt(i);
+
+ // Remove the lint warning below that the current tab might be null.
+ assert newTab != null;
+
+ // Set a custom view on the new tab.
+ newTab.setCustomView(R.layout.tab_custom_view);
+
+ // Add the new page.
+ webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i));
+ }
+
+ // Reset the saved state variables.
+ savedStateArrayList = null;
+ savedNestedScrollWebViewStateArrayList = null;
+
+ // Restore the selected tab position.
+ if (savedTabPosition == 0) { // The first tab is selected.
+ // Set the first page as the current WebView.
+ setCurrentWebView(0);
+ } else { // the first tab is not selected.
+ // Move to the selected tab.
+ webViewPager.setCurrentItem(savedTabPosition);
+ }
+
+ // Process the new intent if it exists.
+ if (newIntentUrl != null) {
+ // Get the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
+ // Set the loading new intent flag.
+ loadingNewIntent = true;
+
+ // Add a new tab.
+ addNewTab(newIntentUrl, true);
+ } else { // Load the URL in the current tab.
+ // Make it so.
+ loadUrl(currentWebView, newIntentUrl);
+ }
+
+ // Reset the new intent URL.
+ newIntentUrl = null;
+ }
+ }
}
public void addTab(View view) {
}
private void addNewTab(String url, boolean moveToTab) {
- // Sanitize the URL.
- url = sanitizeUrl(url);
-
- // Get a handle for the tab layout and the view pager.
- TabLayout tabLayout = findViewById(R.id.tablayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
-
// Get the new page number. The page numbers are 0 indexed, so the new page number will match the current count.
int newTabNumber = tabLayout.getTabCount();
}
public void closeTab(View view) {
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
-
// Run the command according to the number of tabs.
if (tabLayout.getTabCount() > 1) { // There is more than one tab open.
// Close the current tab.
}
private void closeCurrentTab() {
- // Get handles for the views.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
-
// Get the current tab number.
int currentTabNumber = tabLayout.getSelectedTabPosition();
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a green background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
- } else { // The fragment has not been populated. Try again in 100 milliseconds.
+ } else { // The fragment has not been populated. Try again in 100 milliseconds. //TODO try to replace this with a post command.
// Create a handler to set the current WebView.
Handler setCurrentWebViewHandler = new Handler();
}
@Override
- public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
+ public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
} else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
// Turn on the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
}
ActionBar actionBar = appCompatDelegate.getSupportActionBar();
LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
EditText urlEditText = findViewById(R.id.url_edittext);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
// Remove the incorrect lint warning below that the action bar might be null.
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
- }
- // If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
- if (progress >= 50) {
- // Make the current WebView visible.
- currentWebView.setVisibility(View.VISIBLE);
+ // Make the current WebView visible. If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
}
// Get a handle for the navigation menu.
Menu navigationMenu = navigationView.getMenu();
- // Get a handle for the navigation requests menu item. The menu is 0 based.
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ // Get a handle for the navigation requests menu item.
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// Create an empty web resource response to be used if the resource request is blocked.
WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
// Get the preferences.
boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
- // Get a handler for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there.
if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
// No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the stop icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.close_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.close_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_night);
}
}
}
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
}
}
}
});
- // Check to see if this is the first page.
- if (pageNumber == 0) {
+ // Check to see if the state is being restored.
+ if (restoringState) { // The state is being restored.
+ // Resume the nested scroll WebView JavaScript timers.
+ nestedScrollWebView.resumeTimers();
+ } else if (pageNumber == 0) { // The first page is being loaded.
// Set this nested scroll WebView as the current WebView.
currentWebView = nestedScrollWebView;
- // Apply the app settings from the shared preferences.
- applyAppSettings();
-
// Initialize the URL to load string.
String urlToLoadString;
} else if (launchingIntentUriData != null){ // The intent contains a URL.
// Store the URL.
urlToLoadString = launchingIntentUriData.toString();
+ } else if (!url.equals("")) { // The activity has been restarted.
+ // Load the saved URL.
+ urlToLoadString = url;
} else { // The is no URL in the intent.
// Store the homepage to be loaded.
urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
loadUrl(nestedScrollWebView, urlToLoadString);
}
} else { // This is not the first tab.
- // Apply the domain settings.
- applyDomainSettings(nestedScrollWebView, url, false, false);
-
// Load the URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ loadUrl(nestedScrollWebView, url);
// Set the focus and display the keyboard if the URL is blank.
if (url.equals("")) {
import android.widget.Spinner;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar; // The AndroidX toolbar must be used until the minimum API >= 21.
+import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment;
import com.stoutner.privacybrowser.R;
// The resource requests are populated by `MainWebViewActivity` before `RequestsActivity` is launched.
public static List<String[]> resourceRequests;
- // The list view is used in `onCreate()` and `launchViewRequestDialog()`.
+ // Initialize the class constants.
+ private final String LISTVIEW_POSITION = "listview_position";
+
+ // Define the class views.
private ListView requestsListView;
@Override
// Display the view request dialog. The list view is 0 based, so the position must be incremented by 1.
launchViewRequestDialog(position + 1);
});
+
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState != null) {
+ // Scroll to the saved position.
+ requestsListView.post(() -> requestsListView.setSelection(savedInstanceState.getInt(LISTVIEW_POSITION)));
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Get the listview position.
+ int listViewPosition = requestsListView.getFirstVisiblePosition();
+
+ // Store the listview position in the bundle.
+ savedInstanceState.putInt(LISTVIEW_POSITION, listViewPosition);
}
@Override
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.Bundle;
+import android.os.LocaleList;
import android.preference.PreferenceManager;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar; // The AndroidX toolbar must be used until the minimum API is >= 21.
+import androidx.appcompat.widget.Toolbar;
import androidx.core.app.NavUtils;
import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.ViewModelProvider;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import com.google.android.material.snackbar.Snackbar;
import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.asynctasks.GetSource;
import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog;
+import com.stoutner.privacybrowser.helpers.ProxyHelper;
+import com.stoutner.privacybrowser.viewmodelfactories.WebViewSourceFactory;
+import com.stoutner.privacybrowser.viewmodels.WebViewSource;
+
+import java.net.Proxy;
+import java.util.Locale;
public class ViewSourceActivity extends AppCompatActivity {
// `activity` is used in `onCreate()` and `goBack()`.
String userAgent = intent.getStringExtra("user_agent");
String currentUrl = intent.getStringExtra("current_url");
+ // Remove the incorrect lint warning below that the user agent might be null.
+ assert userAgent != null;
+
// Store a handle for the current activity.
activity = this;
// Set the content view.
setContentView(R.layout.view_source_coordinatorlayout);
- // The AndroidX toolbar must be used until the minimum API is >= 21.
+ // Get a handle for the toolbar.
Toolbar toolbar = findViewById(R.id.view_source_toolbar);
+
+ // Set the support action bar.
setSupportActionBar(toolbar);
// Get a handle for the action bar.
actionBar.setCustomView(R.layout.view_source_app_bar);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- // Get a handle for the url text box.
+ // Get handles for the views.
EditText urlEditText = findViewById(R.id.url_edittext);
+ TextView requestHeadersTextView = findViewById(R.id.request_headers);
+ TextView responseMessageTextView = findViewById(R.id.response_message);
+ TextView responseHeadersTextView = findViewById(R.id.response_headers);
+ TextView responseBodyTextView = findViewById(R.id.response_body);
+ ProgressBar progressBar = findViewById(R.id.progress_bar);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.view_source_swiperefreshlayout);
// Populate the URL text box.
urlEditText.setText(currentUrl);
- // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23.
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the red color span according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ } else {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+ }
+
// Apply text highlighting to the URL.
highlightUrlText();
}
});
- // Set the go button on the keyboard to request new source data.
- urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
- // Request new source data if the enter key was pressed.
- if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
- // Hide the soft keyboard.
- inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
+ // Set the refresh color scheme according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
+ }
- // Remove the focus from the URL box.
- urlEditText.clearFocus();
+ // Initialize a color background typed value.
+ TypedValue colorBackgroundTypedValue = new TypedValue();
- // Get the URL.
- String url = urlEditText.getText().toString();
+ // Get the color background from the theme.
+ getTheme().resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true);
+
+ // Get the color background int from the typed value.
+ int colorBackgroundInt = colorBackgroundTypedValue.data;
+
+ // Set the swipe refresh background color.
+ swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt);
- // Get new source data for the current URL if it beings with `http`.
- if (url.startsWith("http")) {
- new GetSource(this, this, userAgent).execute(url);
+ // Get the Do Not Track status.
+ boolean doNotTrack = sharedPreferences.getBoolean("do_not_track", false);
+
+ // Instantiate a locale string.
+ String localeString;
+
+ // Populate the locale string.
+ if (Build.VERSION.SDK_INT >= 24) { // SDK >= 24 has a list of locales.
+ // Get the list of locales.
+ LocaleList localeList = getResources().getConfiguration().getLocales();
+
+ // Initialize a string builder to extract the locales from the list.
+ StringBuilder localesStringBuilder = new StringBuilder();
+
+ // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
+ int q = 10;
+
+ // Populate the string builder with the contents of the locales list.
+ for (int i = 0; i < localeList.size(); i++) {
+ // Append a comma if there is already an item in the string builder.
+ if (i > 0) {
+ localesStringBuilder.append(",");
}
- // Consume the key press.
- return true;
- } else {
- // Do not consume the key press.
- return false;
+ // Get the locale from the list.
+ Locale locale = localeList.get(i);
+
+ // Add the locale to the string. `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
+ localesStringBuilder.append(locale.getLanguage());
+ localesStringBuilder.append("-");
+ localesStringBuilder.append(locale.getCountry());
+
+ // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
+ if (q < 10) {
+ localesStringBuilder.append(";q=0.");
+ localesStringBuilder.append(q);
+ }
+
+ // Decrement `q` if it is greater than 1.
+ if (q > 1) {
+ q--;
+ }
+
+ // Add a second entry for the language only portion of the locale.
+ localesStringBuilder.append(",");
+ localesStringBuilder.append(locale.getLanguage());
+
+ // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
+ localesStringBuilder.append(";q=0.");
+ localesStringBuilder.append(q);
+
+ // Decrement `q` if it is greater than 1.
+ if (q > 1) {
+ q--;
+ }
}
+
+ // Store the populated string builder in the locale string.
+ localeString = localesStringBuilder.toString();
+ } else { // SDK < 24 only has a primary locale.
+ // Store the locale in the locale string.
+ localeString = Locale.getDefault().toString();
+ }
+
+ // Instantiate the proxy helper.
+ ProxyHelper proxyHelper = new ProxyHelper();
+
+ // Get the current proxy.
+ Proxy proxy = proxyHelper.getCurrentProxy(this);
+
+ // Make the progress bar visible.
+ progressBar.setVisibility(View.VISIBLE);
+
+ // Set the progress bar to be indeterminate.
+ progressBar.setIndeterminate(true);
+
+ // Instantiate the WebView source factory.
+ ViewModelProvider.Factory webViewSourceFactory = new WebViewSourceFactory(currentUrl, userAgent, doNotTrack, localeString, proxy, MainWebViewActivity.executorService);
+
+ // Instantiate the WebView source view model class.
+ final WebViewSource webViewSource = new ViewModelProvider(this, webViewSourceFactory).get(WebViewSource.class);
+
+ // Create a source observer.
+ webViewSource.observeSource().observe(this, sourceStringArray -> {
+ // Populate the text views. This can take a long time, and freezes the user interface, if the response body is particularly large.
+ requestHeadersTextView.setText(sourceStringArray[0]);
+ responseMessageTextView.setText(sourceStringArray[1]);
+ responseHeadersTextView.setText(sourceStringArray[2]);
+ responseBodyTextView.setText(sourceStringArray[3]);
+
+ // Hide the progress bar.
+ progressBar.setIndeterminate(false);
+ progressBar.setVisibility(View.GONE);
+
+ //Stop the swipe to refresh indicator if it is running
+ swipeRefreshLayout.setRefreshing(false);
});
- // Get a handle for the swipe refresh layout.
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.view_source_swiperefreshlayout);
+ // Create an error observer.
+ webViewSource.observeErrors().observe(this, errorString -> {
+ // Display an error snackbar if the string is not `""`.
+ if (!errorString.equals("")) {
+ Snackbar.make(swipeRefreshLayout, errorString, Snackbar.LENGTH_LONG).show();
+ }
+ });
// Implement swipe to refresh.
swipeRefreshLayout.setOnRefreshListener(() -> {
+ // Make the progress bar visible.
+ progressBar.setVisibility(View.VISIBLE);
+
+ // Set the progress bar to be indeterminate.
+ progressBar.setIndeterminate(true);
+
// Get the URL.
- String url = urlEditText.getText().toString();
+ String urlString = urlEditText.getText().toString();
- // Get new source data for the URL if it begins with `http`.
- if (url.startsWith("http")) {
- new GetSource(this, this, userAgent).execute(url);
- } else {
- // Stop the refresh animation.
- swipeRefreshLayout.setRefreshing(false);
- }
+ // Get the updated source.
+ webViewSource.updateSource(urlString);
});
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ // Set the go button on the keyboard to request new source data.
+ urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+ // Request new source data if the enter key was pressed.
+ if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+ // Hide the soft keyboard.
+ inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
- // Set the refresh color scheme according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
- } else {
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
- }
+ // Remove the focus from the URL box.
+ urlEditText.clearFocus();
- // Initialize a color background typed value.
- TypedValue colorBackgroundTypedValue = new TypedValue();
+ // Make the progress bar visible.
+ progressBar.setVisibility(View.VISIBLE);
- // Get the color background from the theme.
- getTheme().resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true);
+ // Set the progress bar to be indeterminate.
+ progressBar.setIndeterminate(true);
- // Get the color background int from the typed value.
- int colorBackgroundInt = colorBackgroundTypedValue.data;
+ // Get the URL.
+ String urlString = urlEditText.getText().toString();
- // Set the swipe refresh background color.
- swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt);
+ // Get the updated source.
+ webViewSource.updateSource(urlString);
- // Get the source using an AsyncTask if the URL begins with `http`.
- if ((currentUrl != null) && currentUrl.startsWith("http")) {
- new GetSource(this, this, userAgent).execute(currentUrl);
- }
+ // Consume the key press.
+ return true;
+ } else {
+ // Do not consume the key press.
+ return false;
+ }
+ });
}
@Override
/*
- * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
import com.stoutner.privacybrowser.fragments.AboutTabFragment;
public class AboutPagerAdapter extends FragmentPagerAdapter {
- // Define the class variable to store the blocklist versions.
+ // Define the class variables.
private Context context;
private String[] blocklistVersions;
// Run the default commands.
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
- // Store the context in a class variable.
+ // Store the class variables.
this.context = context;
-
- // Store the blocklist versions in a class variable.
this.blocklistVersions = blocklistVersions;
}
import com.stoutner.privacybrowser.fragments.GuideTabFragment;
public class GuidePagerAdapter extends FragmentPagerAdapter {
- // Define the class context variable.
+ // Define the class variables.
private Context context;
+ // The default constructor.
public GuidePagerAdapter(FragmentManager fragmentManager, Context context) {
// Run the default commands.
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
- // Store the context in a class variable.
+ // Store the class variables.
this.context = context;
}
package com.stoutner.privacybrowser.adapters;
+import android.os.Bundle;
+
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
private LinkedList<WebViewTabFragment> webViewFragmentsList = new LinkedList<>();
// Define the constructor.
- public WebViewPagerAdapter(FragmentManager fragmentManager){
+ public WebViewPagerAdapter(FragmentManager fragmentManager) {
// Run the default commands.
- super(fragmentManager);
+ super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@Override
return position;
}
- public void addPage(int pageNumber, ViewPager webViewPager, String url, boolean moveToTab) {
+ public void addPage(int pageNumber, ViewPager webViewPager, String url, boolean moveToNewPage) {
// Add a new page.
webViewFragmentsList.add(WebViewTabFragment.createPage(pageNumber, url));
// Update the view pager.
notifyDataSetChanged();
- // Move to the new page if it isn't the first one.
- if (pageNumber > 0 && moveToTab) {
+ // Move to the new page if indicated.
+ if (moveToNewPage) {
webViewPager.setCurrentItem(pageNumber);
}
}
+ public void restorePage(Bundle savedState, Bundle savedNestedScrollWebViewState) {
+ // Restore the page.
+ webViewFragmentsList.add(WebViewTabFragment.restorePage(savedState, savedNestedScrollWebViewState));
+
+ // Update the view pager.
+ notifyDataSetChanged();
+ }
+
public boolean deletePage(int pageNumber, ViewPager webViewPager) {
// Delete the page.
webViewFragmentsList.remove(pageNumber);
--- /dev/null
+/*
+ * Copyright © 2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.asynctasks;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.stoutner.privacybrowser.R;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.ref.WeakReference;
+
+// `Void` does not declare any parameters. `Void` does not declare progress units. `String` contains the results.
+public class GetLogcat extends AsyncTask<Void, Void, String> {
+ // Define the class variables.
+ private final WeakReference<Activity> activityWeakReference;
+ private int scrollViewYPositionInt;
+
+ // The public constructor.
+ public GetLogcat(Activity activity, int scrollViewYPositionInt) {
+ // Populate the weak reference to the calling activity.
+ activityWeakReference = new WeakReference<>(activity);
+
+ // Store the scrollview Y position.
+ this.scrollViewYPositionInt = scrollViewYPositionInt;
+ }
+
+ @Override
+ protected String doInBackground(Void... parameters) {
+ // Get a handle for the activity.
+ Activity activity = activityWeakReference.get();
+
+ // Abort if the activity is gone.
+ if ((activity == null) || activity.isFinishing()) {
+ return "";
+ }
+
+ // Create a log string builder.
+ StringBuilder logStringBuilder = new StringBuilder();
+
+ try {
+ // Get the logcat. `-b all` gets all the buffers (instead of just crash, main, and system). `-v long` produces more complete information. `-d` dumps the logcat and exits.
+ Process process = Runtime.getRuntime().exec("logcat -b all -v long -d");
+
+ // Wrap the logcat in a buffered reader.
+ BufferedReader logBufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+ // Create a log transfer string.
+ String logTransferString;
+
+ // Use the log transfer string to copy the logcat from the buffered reader to the string builder.
+ while ((logTransferString = logBufferedReader.readLine()) != null) {
+ // Append a line.
+ logStringBuilder.append(logTransferString);
+
+ // Append a line break.
+ logStringBuilder.append("\n");
+ }
+
+ // Close the buffered reader.
+ logBufferedReader.close();
+ } catch (IOException exception) {
+ // Do nothing.
+ }
+
+ // Return the logcat.
+ return logStringBuilder.toString();
+ }
+
+ // `onPostExecute()` operates on the UI thread.
+ @Override
+ protected void onPostExecute(String logcatString) {
+ // Get a handle for the activity.
+ Activity activity = activityWeakReference.get();
+
+ // Abort if the activity is gone.
+ if ((activity == null) || activity.isFinishing()) {
+ return;
+ }
+
+ // Get handles for the views.
+ TextView logcatTextView = activity.findViewById(R.id.logcat_textview);
+ SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.logcat_swiperefreshlayout);
+ ScrollView scrollView = activity.findViewById(R.id.logcat_scrollview);
+
+ // Display the logcat.
+ logcatTextView.setText(logcatString);
+
+ // Update the scroll position after the text is populated.
+ logcatTextView.post(() -> {
+ // Set the scroll position.
+ scrollView.setScrollY(scrollViewYPositionInt);
+ });
+
+ // Stop the swipe to refresh animation if it is displayed.
+ swipeRefreshLayout.setRefreshing(false);
+ }
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.asynctasks;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.Typeface;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.LocaleList;
-import android.preference.PreferenceManager;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.StyleSpan;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.helpers.ProxyHelper;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.URL;
-import java.util.Locale;
-
-// This must run asynchronously because it involves a network request. `String` declares the parameters. `Void` does not declare progress units. `SpannableStringBuilder[]` contains the results.
-public class GetSource extends AsyncTask<String, Void, SpannableStringBuilder[]> {
- // Define weak references to the calling context and activity.
- private WeakReference<Context> contextWeakReference;
- private WeakReference<Activity> activityWeakReference;
-
- // Store the user agent.
- private String userAgent;
-
- public GetSource(Context context, Activity activity, String userAgent) {
- // Populate the weak references to the calling context and activity.
- contextWeakReference = new WeakReference<>(context);
- activityWeakReference = new WeakReference<>(activity);
-
- // Store the user agent.
- this.userAgent = userAgent;
- }
-
- // `onPreExecute()` operates on the UI thread.
- @Override
- protected void onPreExecute() {
- // Get a handle for the calling activity.
- Activity activity = activityWeakReference.get();
-
- // Abort if the activity is gone.
- if ((activity == null) || activity.isFinishing()) {
- return;
- }
-
- // Get a handle for the progress bar.
- ProgressBar progressBar = activity.findViewById(R.id.progress_bar);
-
- // Make the progress bar visible.
- progressBar.setVisibility(View.VISIBLE);
-
- // Set the progress bar to be indeterminate.
- progressBar.setIndeterminate(true);
- }
-
- @Override
- protected SpannableStringBuilder[] doInBackground(String... formattedUrlString) {
- // Initialize the response body String.
- SpannableStringBuilder requestHeadersBuilder = new SpannableStringBuilder();
- SpannableStringBuilder responseMessageBuilder = new SpannableStringBuilder();
- SpannableStringBuilder responseHeadersBuilder = new SpannableStringBuilder();
- SpannableStringBuilder responseBodyBuilder = new SpannableStringBuilder();
-
- // Get a handle for the context and activity.
- Context context = contextWeakReference.get();
- Activity activity = activityWeakReference.get();
-
- // Abort if the activity is gone.
- if ((activity == null) || activity.isFinishing()) {
- return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
- }
-
- // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
- try {
- // Get the current URL from the main activity.
- URL url = new URL(formattedUrlString[0]);
-
- // Instantiate the proxy helper.
- ProxyHelper proxyHelper = new ProxyHelper();
-
- // Get the current proxy.
- Proxy proxy = proxyHelper.getCurrentProxy(context);
-
- // Open a connection to the URL. No data is actually sent at this point.
- HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection(proxy);
-
- // Define the variables necessary to build the request headers.
- requestHeadersBuilder = new SpannableStringBuilder();
- int oldRequestHeadersBuilderLength;
- int newRequestHeadersBuilderLength;
-
-
- // Set the `Host` header property.
- httpUrlConnection.setRequestProperty("Host", url.getHost());
-
- // Add the `Host` header to the string builder and format the text.
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Host", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Host");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
- requestHeadersBuilder.append(url.getHost());
-
-
- // Set the `Connection` header property.
- httpUrlConnection.setRequestProperty("Connection", "keep-alive");
-
- // Add the `Connection` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Connection", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Connection");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": keep-alive");
-
-
- // Set the `Upgrade-Insecure-Requests` header property.
- httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
-
- // Add the `Upgrade-Insecure-Requests` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Upgrade-Insecure-Requests", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Upgrade-Insecure_Requests");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": 1");
-
-
- // Set the `User-Agent` header property.
- httpUrlConnection.setRequestProperty("User-Agent", userAgent);
-
- // Add the `User-Agent` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("User-Agent", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("User-Agent");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
- requestHeadersBuilder.append(userAgent);
-
-
- // Set the `x-requested-with` header property.
- httpUrlConnection.setRequestProperty("x-requested-with", "");
-
- // Add the `x-requested-with` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("x-requested-with", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("x-requested-with");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
-
-
- // Set the `Sec-Fetch-Site` header property.
- httpUrlConnection.setRequestProperty("Sec-Fetch-Site", "none");
-
- // Add the `Sec-Fetch-Site` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Sec-Fetch-Site", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Sec-Fetch-Site");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": none");
-
-
- // Set the `Sec-Fetch-Mode` header property.
- httpUrlConnection.setRequestProperty("Sec-Fetch-Mode", "navigate");
-
- // Add the `Sec-Fetch-Mode` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Sec-Fetch-Mode", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Sec-Fetch-Mode");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": navigate");
-
-
- // Set the `Sec-Fetch-User` header property.
- httpUrlConnection.setRequestProperty("Sec-Fetch-User", "?1");
-
- // Add the `Sec-Fetch-User` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Sec-Fetch-User", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Sec-Fetch-User");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ?1");
-
-
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
-
- // Only populate `Do Not Track` if it is enabled.
- if (sharedPreferences.getBoolean("do_not_track", false)) {
- // Set the `dnt` header property.
- httpUrlConnection.setRequestProperty("dnt", "1");
-
- // Add the `dnt` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("dnt");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": 1");
- }
-
-
- // Set the `Accept` header property.
- httpUrlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
-
- // Add the `Accept` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Accept");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
- requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
-
-
- // Instantiate a locale string.
- String localeString;
-
- // Populate the locale string.
- if (Build.VERSION.SDK_INT >= 24) { // SDK >= 24 has a list of locales.
- // Get the list of locales.
- LocaleList localeList = activity.getResources().getConfiguration().getLocales();
-
- // Initialize a string builder to extract the locales from the list.
- StringBuilder localesStringBuilder = new StringBuilder();
-
- // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
- int q = 10;
-
- // Populate the string builder with the contents of the locales list.
- for (int i = 0; i < localeList.size(); i++) {
- // Append a comma if there is already an item in the string builder.
- if (i > 0) {
- localesStringBuilder.append(",");
- }
-
- // Get the locale from the list.
- Locale locale = localeList.get(i);
-
- // Add the locale to the string. `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
- localesStringBuilder.append(locale.getLanguage());
- localesStringBuilder.append("-");
- localesStringBuilder.append(locale.getCountry());
-
- // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
- if (q < 10) {
- localesStringBuilder.append(";q=0.");
- localesStringBuilder.append(q);
- }
-
- // Decrement `q` if it is greater than 1.
- if (q > 1) {
- q--;
- }
-
- // Add a second entry for the language only portion of the locale.
- localesStringBuilder.append(",");
- localesStringBuilder.append(locale.getLanguage());
-
- // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
- localesStringBuilder.append(";q=0.");
- localesStringBuilder.append(q);
-
- // Decrement `q` if it is greater than 1.
- if (q > 1) {
- q--;
- }
- }
-
- // Store the populated string builder in the locale string.
- localeString = localesStringBuilder.toString();
- } else { // SDK < 24 only has a primary locale.
- // Store the locale in the locale string.
- localeString = Locale.getDefault().toString();
- }
-
- // Set the `Accept-Language` header property.
- httpUrlConnection.setRequestProperty("Accept-Language", localeString);
-
- // Add the `Accept-Language` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Accept-Language");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
- requestHeadersBuilder.append(localeString);
-
-
- // Get the cookies for the current domain.
- String cookiesString = CookieManager.getInstance().getCookie(url.toString());
-
- // Only process the cookies if they are not null.
- if (cookiesString != null) {
- // Add the cookies to the header property.
- httpUrlConnection.setRequestProperty("Cookie", cookiesString);
-
- // Add the cookie header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Cookie");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": ");
- requestHeadersBuilder.append(cookiesString);
- }
-
-
- // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default. If the property is manually set, than `HttpUrlConnection` does not process the decoding.
- // Add the `Accept-Encoding` header to the string builder and format the text.
- requestHeadersBuilder.append(System.getProperty("line.separator"));
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.append("Accept-Encoding");
- newRequestHeadersBuilderLength = requestHeadersBuilder.length();
- requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- requestHeadersBuilder.append(": gzip");
-
-
- // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block.
- try {
- // Initialize the string builders.
- responseMessageBuilder = new SpannableStringBuilder();
- responseHeadersBuilder = new SpannableStringBuilder();
-
- // Get the response code, which causes the connection to the server to be made.
- int responseCode = httpUrlConnection.getResponseCode();
-
- // Populate the response message string builder.
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- responseMessageBuilder.append(String.valueOf(responseCode));
- int newLength = responseMessageBuilder.length();
- responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- responseMessageBuilder.append(": ");
- responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
-
- // Initialize the iteration variable.
- int i = 0;
-
- // Iterate through the received header fields.
- while (httpUrlConnection.getHeaderField(i) != null) {
- // Add a new line if there is already information in the string builder.
- if (i > 0) {
- responseHeadersBuilder.append(System.getProperty("line.separator"));
- }
-
- // Add the header to the string builder and format the text.
- if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
- responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else { // Older versions not so much.
- int oldLength = responseHeadersBuilder.length();
- responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
- int newLength = responseHeadersBuilder.length();
- responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- responseHeadersBuilder.append(": ");
- responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
-
- // Increment the iteration variable.
- i++;
- }
-
- // Instantiate an input stream for the response body.
- InputStream inputStream;
-
- // Get the correct input stream based on the response code.
- if (responseCode == 404) { // Get the error stream.
- inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
- } else { // Get the response body stream.
- inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
- }
-
- // Initialize the byte array output stream and the conversion buffer byte array.
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byte[] conversionBufferByteArray = new byte[1024];
-
- // Define the buffer length variable.
- int bufferLength;
-
- try {
- // Attempt to read data from the input stream and store it in the conversion buffer byte array. Also store the amount of data read in the buffer length variable.
- while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) { // Proceed while the amount of data stored in the buffer is > 0.
- // Write the contents of the conversion buffer to the byte array output stream.
- byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
- }
- } catch (IOException exception) {
- // Do nothing.
- }
-
- // Close the input stream.
- inputStream.close();
-
- // Populate the response body string with the contents of the byte array output stream.
- responseBodyBuilder.append(byteArrayOutputStream.toString());
- } finally {
- // Disconnect HTTP URL connection.
- httpUrlConnection.disconnect();
- }
- } catch (Exception exception) {
- // Do nothing.
- }
-
- // Return the response body string as the result.
- return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
- }
-
- // `onPostExecute()` operates on the UI thread.
- @Override
- protected void onPostExecute(SpannableStringBuilder[] viewSourceStringArray){
- // Get a handle for the activity.
- Activity activity = activityWeakReference.get();
-
- // Abort if the activity is gone.
- if ((activity == null) || activity.isFinishing()) {
- return;
- }
-
- // Get handles for the text views.
- TextView requestHeadersTextView = activity.findViewById(R.id.request_headers);
- TextView responseMessageTextView = activity.findViewById(R.id.response_message);
- TextView responseHeadersTextView = activity.findViewById(R.id.response_headers);
- TextView responseBodyTextView = activity.findViewById(R.id.response_body);
- ProgressBar progressBar = activity.findViewById(R.id.progress_bar);
- SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.view_source_swiperefreshlayout);
-
- // Populate the text views. This can take a long time, and freeze the user interface, if the response body is particularly large.
- requestHeadersTextView.setText(viewSourceStringArray[0]);
- responseMessageTextView.setText(viewSourceStringArray[1]);
- responseHeadersTextView.setText(viewSourceStringArray[2]);
- responseBodyTextView.setText(viewSourceStringArray[3]);
-
- // Hide the progress bar.
- progressBar.setIndeterminate(false);
- progressBar.setVisibility(View.GONE);
-
- //Stop the swipe to refresh indicator if it is running
- swipeRefreshLayout.setRefreshing(false);
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.backgroundtasks;
+
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+import android.webkit.CookieManager;
+
+import com.stoutner.privacybrowser.viewmodels.WebViewSource;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+
+public class GetSourceBackgroundTask {
+ public SpannableStringBuilder[] acquire(String urlString, String userAgent, boolean doNotTrack, String localeString, Proxy proxy, WebViewSource webViewSource) {
+ // Initialize the spannable string builders.
+ SpannableStringBuilder requestHeadersBuilder = new SpannableStringBuilder();
+ SpannableStringBuilder responseMessageBuilder = new SpannableStringBuilder();
+ SpannableStringBuilder responseHeadersBuilder = new SpannableStringBuilder();
+ SpannableStringBuilder responseBodyBuilder = new SpannableStringBuilder();
+
+ // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
+ try {
+ // Get the current URL from the main activity.
+ URL url = new URL(urlString);
+
+ // Open a connection to the URL. No data is actually sent at this point.
+ HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection(proxy);
+
+ // Define the variables necessary to build the request headers.
+ requestHeadersBuilder = new SpannableStringBuilder();
+ int oldRequestHeadersBuilderLength;
+ int newRequestHeadersBuilderLength;
+
+
+ // Set the `Host` header property.
+ httpUrlConnection.setRequestProperty("Host", url.getHost());
+
+ // Add the `Host` header to the string builder and format the text.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Host", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Host");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(url.getHost());
+
+
+ // Set the `Connection` header property.
+ httpUrlConnection.setRequestProperty("Connection", "keep-alive");
+
+ // Add the `Connection` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Connection", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Connection");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": keep-alive");
+
+
+ // Set the `Upgrade-Insecure-Requests` header property.
+ httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
+
+ // Add the `Upgrade-Insecure-Requests` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Upgrade-Insecure-Requests", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Upgrade-Insecure_Requests");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": 1");
+
+
+ // Set the `User-Agent` header property.
+ httpUrlConnection.setRequestProperty("User-Agent", userAgent);
+
+ // Add the `User-Agent` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("User-Agent", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("User-Agent");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(userAgent);
+
+
+ // Set the `x-requested-with` header property.
+ httpUrlConnection.setRequestProperty("x-requested-with", "");
+
+ // Add the `x-requested-with` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("x-requested-with", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("x-requested-with");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+
+
+ // Set the `Sec-Fetch-Site` header property.
+ httpUrlConnection.setRequestProperty("Sec-Fetch-Site", "none");
+
+ // Add the `Sec-Fetch-Site` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Sec-Fetch-Site", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Sec-Fetch-Site");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": none");
+
+
+ // Set the `Sec-Fetch-Mode` header property.
+ httpUrlConnection.setRequestProperty("Sec-Fetch-Mode", "navigate");
+
+ // Add the `Sec-Fetch-Mode` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Sec-Fetch-Mode", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Sec-Fetch-Mode");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": navigate");
+
+
+ // Set the `Sec-Fetch-User` header property.
+ httpUrlConnection.setRequestProperty("Sec-Fetch-User", "?1");
+
+ // Add the `Sec-Fetch-User` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Sec-Fetch-User", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Sec-Fetch-User");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ?1");
+
+
+ // Only populate `Do Not Track` if it is enabled.
+ if (doNotTrack) {
+ // Set the `dnt` header property.
+ httpUrlConnection.setRequestProperty("dnt", "1");
+
+ // Add the `dnt` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("dnt");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": 1");
+ }
+
+
+ // Set the `Accept` header property.
+ httpUrlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
+
+ // Add the `Accept` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
+
+
+ // Set the `Accept-Language` header property.
+ httpUrlConnection.setRequestProperty("Accept-Language", localeString);
+
+ // Add the `Accept-Language` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept-Language");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(localeString);
+
+
+ // Get the cookies for the current domain.
+ String cookiesString = CookieManager.getInstance().getCookie(url.toString());
+
+ // Only process the cookies if they are not null.
+ if (cookiesString != null) {
+ // Add the cookies to the header property.
+ httpUrlConnection.setRequestProperty("Cookie", cookiesString);
+
+ // Add the cookie header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Cookie");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(cookiesString);
+ }
+
+
+ // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default. If the property is manually set, than `HttpUrlConnection` does not process the decoding.
+ // Add the `Accept-Encoding` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept-Encoding");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": gzip");
+
+
+ // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block.
+ try {
+ // Initialize the string builders.
+ responseMessageBuilder = new SpannableStringBuilder();
+ responseHeadersBuilder = new SpannableStringBuilder();
+
+ // Get the response code, which causes the connection to the server to be made.
+ int responseCode = httpUrlConnection.getResponseCode();
+
+ // Populate the response message string builder.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ responseMessageBuilder.append(String.valueOf(responseCode));
+ int newLength = responseMessageBuilder.length();
+ responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ responseMessageBuilder.append(": ");
+ responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
+
+ // Initialize the iteration variable.
+ int i = 0;
+
+ // Iterate through the received header fields.
+ while (httpUrlConnection.getHeaderField(i) != null) {
+ // Add a new line if there is already information in the string builder.
+ if (i > 0) {
+ responseHeadersBuilder.append(System.getProperty("line.separator"));
+ }
+
+ // Add the header to the string builder and format the text.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ int oldLength = responseHeadersBuilder.length();
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
+ int newLength = responseHeadersBuilder.length();
+ responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ responseHeadersBuilder.append(": ");
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
+
+ // Increment the iteration variable.
+ i++;
+ }
+
+ // Instantiate an input stream for the response body.
+ InputStream inputStream;
+
+ // Get the correct input stream based on the response code.
+ if (responseCode == 404) { // Get the error stream.
+ inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
+ } else { // Get the response body stream.
+ inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
+ }
+
+ // Initialize the byte array output stream and the conversion buffer byte array.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byte[] conversionBufferByteArray = new byte[1024];
+
+ // Define the buffer length variable.
+ int bufferLength;
+
+ try {
+ // Attempt to read data from the input stream and store it in the conversion buffer byte array. Also store the amount of data read in the buffer length variable.
+ while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) { // Proceed while the amount of data stored in the buffer is > 0.
+ // Write the contents of the conversion buffer to the byte array output stream.
+ byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
+ }
+ } catch (IOException exception) {
+ // Return the error message.
+ webViewSource.returnError(exception.toString());
+ }
+
+ // Close the input stream.
+ inputStream.close();
+
+ // Populate the response body string with the contents of the byte array output stream.
+ responseBodyBuilder.append(byteArrayOutputStream.toString());
+ } finally {
+ // Disconnect HTTP URL connection.
+ httpUrlConnection.disconnect();
+ }
+ } catch (Exception exception) {
+ // Return the error message.
+ webViewSource.returnError(exception.toString());
+ }
+
+ // Return the response body string as the result.
+ return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
+ }
+}
val matrixCursor = MatrixCursor(matrixCursorColumnNamesArray)
// Add `Home Folder` as the first entry in the matrix folder.
- matrixCursor.addRow(arrayOf<Any>(BookmarksDatabaseViewActivity.HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)))
+ matrixCursor.addRow(arrayOf(BookmarksDatabaseViewActivity.HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)))
// Get a cursor with the list of all the folders.
val foldersCursor = bookmarksDatabaseHelper.allFolders
}
})
- // Update the edit button if the folder changes.
- folderSpinner.onItemSelectedListener = object: OnItemSelectedListener {
- override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
- // Update the edit button.
- updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
- }
+ // Wait to set the on item selected listener until the spinner has been inflated. Otherwise the dialog will crash on restart.
+ folderSpinner.post {
+ // Update the edit button if the folder changes.
+ folderSpinner.onItemSelectedListener = object : OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
+ // Update the edit button.
+ updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+ }
- override fun onNothingSelected(parent: AdapterView<*>?) {
- // Do nothing.
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ // Do nothing.
+ }
}
}
ForegroundColorSpan redColorSpan;
// Set the color spans according to the theme. The deprecated `getResources()` must be used until the minimum API >= 23.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ } else {
+ blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
}
// Setup the spans to display the certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
// Add each IP address to the string builder.
for (InetAddress inetAddress : inetAddressesArray) {
- if (ipAddresses.length() == 0) { // This is the first IP address.
- // Add the IP Address to the string builder.
- ipAddresses.append(inetAddress.getHostAddress());
- } else { // This is not the first IP address.
+ // Check to see if this is not the first IP address.
+ if (ipAddresses.length() > 0) {
// Add a line break to the string builder first.
ipAddresses.append("\n");
-
- // Add the IP address to the string builder.
- ipAddresses.append(inetAddress.getHostAddress());
}
+
+ // Add the IP Address to the string builder.
+ ipAddresses.append(inetAddress.getHostAddress());
}
} catch (UnknownHostException exception) {
// Do nothing.
import java.util.Date;
public class AboutTabFragment extends Fragment {
- // Declare the class variables.
+ // Define the class variables.
private int tabNumber;
private String[] blocklistVersions;
+ private View tabLayout;
public static AboutTabFragment createTab(int tabNumber, String[] blocklistVersions) {
// Create a bundle.
@Override
public View onCreateView(@NonNull LayoutInflater layoutInflater, ViewGroup container, Bundle savedInstanceState) {
- // Create a tab layout view.
- View tabLayout;
-
// Get a handle for the context and assert that it isn't null.
Context context = getContext();
assert context != null;
}
}
+ // Scroll the tab if the saved instance state is not null.
+ if (savedInstanceState != null) {
+ tabLayout.post(() -> {
+ tabLayout.setScrollX(savedInstanceState.getInt("scroll_x"));
+ tabLayout.setScrollY(savedInstanceState.getInt("scroll_y"));
+ });
+ }
+
// Return the formatted `tabLayout`.
return tabLayout;
}
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Save the scroll positions if the tab layout is not null, which can happen if a tab is not currently selected.
+ if (tabLayout != null) {
+ savedInstanceState.putInt("scroll_x", tabLayout.getScrollX());
+ savedInstanceState.putInt("scroll_y", tabLayout.getScrollY());
+ }
+ }
}
\ No newline at end of file
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
+import android.widget.ScrollView;
import android.widget.Spinner;
-import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.appcompat.widget.SwitchCompat;
import androidx.cardview.widget.CardView;
+import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment; // The AndroidX fragment must be used until minimum API >= 23. Otherwise `getContext()` does not work.
import com.stoutner.privacybrowser.R;
import java.util.Date;
public class DomainSettingsFragment extends Fragment {
- // `DATABASE_ID` is used by activities calling this fragment.
+ // Initialize the public class constants. These are used by activities calling this fragment.
public static final String DATABASE_ID = "database_id";
+ public static final String SCROLL_Y = "scroll_y";
- // `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
+ // Define the public variables. `databaseId` is public static so it can be accessed from `DomainsActivity`. It is also used in `onCreate()` and `onCreateView()`.
public static int databaseId;
+ // Define the class variables.
+ private int scrollY;
+
@Override
public void onCreate(Bundle savedInstanceState) {
// Run the default commands.
// Store the database id in `databaseId`.
databaseId = getArguments().getInt(DATABASE_ID);
+ scrollY = getArguments().getInt(SCROLL_Y);
}
// The deprecated `getDrawable()` must be used until the minimum API >= 21.
// Inflate `domain_settings_fragment`. `false` does not attach it to the root `container`.
View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false);
- // Get a handle for the context and the resources.
+ // Get handles for the context and the resources.
Context context = getContext();
Resources resources = getResources();
boolean defaultWideViewport = sharedPreferences.getBoolean("wide_viewport", true);
boolean defaultDisplayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
- // Get handles for the views in the fragment.
+ // Get handles for the views.
+ ScrollView domainSettingsScrollView = domainSettingsView.findViewById(R.id.domain_settings_scrollview);
EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext);
ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview);
- Switch javaScriptSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
+ SwitchCompat javaScriptSwitch = domainSettingsView.findViewById(R.id.javascript_switch);
ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.first_party_cookies_imageview);
- Switch firstPartyCookiesSwitch = domainSettingsView.findViewById(R.id.first_party_cookies_switch);
+ SwitchCompat firstPartyCookiesSwitch = domainSettingsView.findViewById(R.id.first_party_cookies_switch);
LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.third_party_cookies_linearlayout);
ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.third_party_cookies_imageview);
- Switch thirdPartyCookiesSwitch = domainSettingsView.findViewById(R.id.third_party_cookies_switch);
+ SwitchCompat thirdPartyCookiesSwitch = domainSettingsView.findViewById(R.id.third_party_cookies_switch);
ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview);
- Switch domStorageSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
+ SwitchCompat domStorageSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch);
ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview); // The form data views can be remove once the minimum API >= 26.
- Switch formDataSwitch = domainSettingsView.findViewById(R.id.form_data_switch); // The form data views can be remove once the minimum API >= 26.
+ SwitchCompat formDataSwitch = domainSettingsView.findViewById(R.id.form_data_switch); // The form data views can be remove once the minimum API >= 26.
ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview);
- Switch easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
+ SwitchCompat easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch);
ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview);
- Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
+ SwitchCompat easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch);
ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview);
- Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
+ SwitchCompat fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch);
ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview);
- Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
+ SwitchCompat fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch);
ImageView ultraListImageView = domainSettingsView.findViewById(R.id.ultralist_imageview);
- Switch ultraListSwitch = domainSettingsView.findViewById(R.id.ultralist_switch);
+ SwitchCompat ultraListSwitch = domainSettingsView.findViewById(R.id.ultralist_switch);
ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview);
- Switch ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
+ SwitchCompat ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch);
ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview);
- Switch blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
+ SwitchCompat blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch);
Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner);
TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview);
EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext);
Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.display_webpage_images_spinner);
TextView displayImagesTextView = domainSettingsView.findViewById(R.id.display_webpage_images_textview);
ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_imageview);
- Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch);
+ SwitchCompat pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch);
CardView savedSslCardView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_cardview);
LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout);
RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton);
TextView currentSslEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date);
TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate);
ImageView pinnedIpAddressesImageView = domainSettingsView.findViewById(R.id.pinned_ip_addresses_imageview);
- Switch pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch);
+ SwitchCompat pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch);
CardView savedIpAddressesCardView = domainSettingsView.findViewById(R.id.saved_ip_addresses_cardview);
LinearLayout savedIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.saved_ip_addresses_linearlayout);
RadioButton savedIpAddressesRadioButton = domainSettingsView.findViewById(R.id.saved_ip_addresses_radiobutton);
// Set the JavaScript switch status.
if (javaScriptInt == 1) { // JavaScript is enabled.
javaScriptSwitch.setChecked(true);
- javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
+ javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
} else { // JavaScript is disabled.
javaScriptSwitch.setChecked(false);
- javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
+ javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
}
// Set the first-party cookies status. Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
if (firstPartyCookiesInt == 1) { // First-party cookies are enabled.
firstPartyCookiesSwitch.setChecked(true);
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_enabled, null));
} else { // First-party cookies are disabled.
firstPartyCookiesSwitch.setChecked(false);
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_night));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
} else {
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_day));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
}
}
thirdPartyCookiesSwitch.setChecked(true);
// Set the icon to be red.
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_warning, null));
} else { // First party cookies are enabled but third-party cookies are disabled.
// Set the third-party cookies switch to be checked.
thirdPartyCookiesSwitch.setChecked(false);
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_night));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
} else {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_day));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
}
}
} else { // First-party cookies are disabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_night));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_night, null));
} else {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_day));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_day, null));
}
}
} else { // Third-party cookies cannot be configured for API <= 21.
// Set the DOM storage status. Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
if (domStorageInt == 1) { // Both JavaScript and DOM storage are enabled.
domStorageSwitch.setChecked(true);
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
} else { // JavaScript is enabled but DOM storage is disabled.
// Set the DOM storage switch to off.
domStorageSwitch.setChecked(false);
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_night));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
} else {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_day));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
}
}
} else { // JavaScript is disabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_night));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_night, null));
} else {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_day));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_day, null));
}
}
formDataSwitch.setChecked(true);
// Set the form data icon.
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
} else { // Form data is off.
// Turn the form data switch to off.
formDataSwitch.setChecked(false);
// Set the icon according to the theme. Once the minimum API >= 21 a selector can be used as the tint mode instead of specifying different icons.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_night));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_night, null));
} else {
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_day));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_day, null));
}
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_night));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
} else {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_day));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
}
} else { // EasyList is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_night));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
} else {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_day));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_night));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
} else {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_day));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
}
} else { // EasyPrivacy is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_night));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
} else {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_day));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_night));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
} else {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_day));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
}
} else { // Fanboy's Annoyance List is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_night));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
} else {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_day));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
}
} else { // Fanboy's Social Blocking List is off.
// Turn off Fanboy's Social Blocking List switch.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
}
}
} else { // Fanboy's Annoyance List is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_night));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
} else {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_day));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
}
} else { // UltraList is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_night));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
} else {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_day));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_night));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
} else {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_day));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
}
} else { // EasyPrivacy is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_night));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
} else {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_day));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_night));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_night, null));
} else {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_day));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_day, null));
}
} else { // Blocking all third-party requests is off.
// Turn the switch off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_night));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_night, null));
} else {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_day));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_day, null));
}
}
if (defaultSwipeToRefresh) { // Swipe to refresh is enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
}
} else { // Swipe to refresh is disabled by default
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
}
// Hide the swipe to refresh TextView.`
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
}
// Hide the swipe to refresh TextView.
swipeToRefreshTextView.setVisibility(View.GONE);
+ break;
}
// Open the swipe to refresh spinner when the TextView is clicked.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
// Set the light mode icon.
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
// Set the dark theme icon.
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
}
break;
case DomainsDatabaseHelper.LIGHT_THEME: // the default WebView theme is light.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
}
break;
case DomainsDatabaseHelper.DARK_THEME: // the default WebView theme is dark.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
- }
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null)); }
break;
}
case DomainsDatabaseHelper.LIGHT_THEME: // The domain WebView theme is light.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
}
// Hide the WebView theme text view.
case DomainsDatabaseHelper.DARK_THEME: // The domain WebView theme is dark.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
}
// Hide the WebView theme text view.
if (defaultWideViewport) { // Wide viewport enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
}
} else { // Wide viewport disabled by default.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
}
// Hide the wide viewport text view.
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
}
// Hide the wide viewport text view.
if (defaultDisplayWebpageImages) { // Display webpage images enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
}
} else { // Display webpage images disabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
}
// Hide the display images text view.
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
}
// Hide the display images text view.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_night));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
} else {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_day));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
}
} else { // Pinned SSL certificate is disabled.
// Uncheck the switch.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_night));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
} else {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_day));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
}
}
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_night));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
} else {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_day));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
}
} else { // Pinned IP Addresses is disabled.
// Uncheck the switch.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_night));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
} else {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_day));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
}
}
javaScriptSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
if (isChecked) { // JavaScript is enabled.
// Update the JavaScript icon.
- javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled));
+ javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.javascript_enabled, null));
// Enable the DOM storage `Switch`.
domStorageSwitch.setEnabled(true);
// Update the DOM storage icon.
if (domStorageSwitch.isChecked()) { // DOM storage is enabled.
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
} else { // DOM storage is disabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_night));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
} else {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_day));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
}
}
} else { // JavaScript is disabled.
// Update the JavaScript icon.
- javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode));
+ javaScriptImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.privacy_mode, null));
// Disable the DOM storage `Switch`.
domStorageSwitch.setEnabled(false);
// Set the DOM storage icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_night));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_night, null));
} else {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_day));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_ghosted_day, null));
}
}
});
firstPartyCookiesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
if (isChecked) { // First-party cookies are enabled.
// Update the first-party cookies icon.
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_enabled));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_enabled, null));
// Enable the third-party cookies switch.
thirdPartyCookiesSwitch.setEnabled(true);
// Update the third-party cookies icon.
if (thirdPartyCookiesSwitch.isChecked()) { // Third-party cookies are enabled.
// Set the third-party cookies icon to be red.
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_warning, null));
} else { // Third-party cookies are disabled.
// Set the third-party cookies icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_night));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
} else {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_day));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
}
}
} else { // First-party cookies are disabled.
// Update the first-party cookies icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_night));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
} else {
- firstPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_day));
+ firstPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
}
// Disable the third-party cookies switch.
// Set the third-party cookies icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_night));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_night, null));
} else {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_ghosted_day));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_ghosted_day, null));
}
}
});
// Update the icon.
if (isChecked) {
// Set the third-party cookies icon to be red.
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_warning));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_warning, null));
} else {
// Update the third-party cookies icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_night));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_night, null));
} else {
- thirdPartyCookiesImageView.setImageDrawable(resources.getDrawable(R.drawable.cookies_disabled_day));
+ thirdPartyCookiesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cookies_disabled_day, null));
}
}
});
domStorageSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
// Update the icon.
if (isChecked) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_enabled, null));
} else {
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_night));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_night, null));
} else {
- domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_day));
+ domStorageImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.dom_storage_disabled_day, null));
}
}
});
formDataSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> {
// Update the icon.
if (isChecked) {
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_enabled));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_enabled, null));
} else {
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_night));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_night, null));
} else {
- formDataImageView.setImageDrawable(resources.getDrawable(R.drawable.form_data_disabled_day));
+ formDataImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.form_data_disabled_day, null));
}
}
});
if (isChecked) { // EasyList is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_night));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
} else {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_day));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
}
} else { // EasyList is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_night));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
} else {
- easyListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_day));
+ easyListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
}
}
});
if (isChecked) { // EasyPrivacy is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_night));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
} else {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_day));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
}
} else { // EasyPrivacy is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_night));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
} else {
- easyPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_day));
+ easyPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
}
}
});
if (isChecked) { // Fanboy's Annoyance List is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_night));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
} else {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_day));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
}
// Disable the Fanboy's Social Blocking List switch.
// Update the Fanboy's Social Blocking List icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_ghosted_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_ghosted_day, null));
}
} else { // Fanboy's Annoyance List is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_night));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
} else {
- fanboysAnnoyanceListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_day));
+ fanboysAnnoyanceListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
}
// Enable the Fanboy's Social Blocking List switch.
if (fanboysSocialBlockingListSwitch.isChecked()) { // Fanboy's Social Blocking List is on.
// Update the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
}
} else { // Fanboy's Social Blocking List is off.
// Update the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
}
}
}
if (isChecked) { // Fanboy's Social Blocking List is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_enabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_enabled_day, null));
}
} else { // Fanboy's Social Blocking List is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_night));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_night, null));
} else {
- fanboysSocialBlockingListImageView.setImageDrawable(resources.getDrawable(R.drawable.social_media_disabled_day));
+ fanboysSocialBlockingListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.social_media_disabled_day, null));
}
}
});
if (isChecked) { // UltraList is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_night));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_night, null));
} else {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_enabled_day));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_enabled_day, null));
}
} else { // UltraList is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_night));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_night, null));
} else {
- ultraListImageView.setImageDrawable(resources.getDrawable(R.drawable.block_ads_disabled_day));
+ ultraListImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_ads_disabled_day, null));
}
}
});
if (isChecked) { // UltraPrivacy is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_night));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_night, null));
} else {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_enabled_day));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_enabled_day, null));
}
} else { // UltraPrivacy is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_night));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_night, null));
} else {
- ultraPrivacyImageView.setImageDrawable(resources.getDrawable(R.drawable.block_tracking_disabled_day));
+ ultraPrivacyImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_tracking_disabled_day, null));
}
}
});
if (isChecked) { // Blocking all third-party requests is on.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_night));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_night, null));
} else {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_enabled_day));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_enabled_day, null));
}
} else { // Blocking all third-party requests is off.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_night));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_night, null));
} else {
- blockAllThirdPartyRequestsImageView.setImageDrawable(resources.getDrawable(R.drawable.block_all_third_party_requests_disabled_day));
+ blockAllThirdPartyRequestsImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.block_all_third_party_requests_disabled_day, null));
}
}
});
if (defaultSwipeToRefresh) { // Swipe to refresh enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
}
} else { // Swipe to refresh disabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_enabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_enabled_day, null));
}
// Hide the swipe to refresh TextView.
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_night));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_night, null));
} else {
- swipeToRefreshImageView.setImageDrawable(resources.getDrawable(R.drawable.refresh_disabled_day));
+ swipeToRefreshImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.refresh_disabled_day, null));
}
// Hide the swipe to refresh TextView.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
// Set the light mode icon.
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
// Set the dark theme icon.
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
}
break;
case DomainsDatabaseHelper.LIGHT_THEME: // The default WebView theme is light.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
}
break;
case DomainsDatabaseHelper.DARK_THEME: // The default WebView theme is dark.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
}
break;
}
case DomainsDatabaseHelper.LIGHT_THEME: // The domain WebView theme is light.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_light_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_light_theme_night, null));
}
// Hide the WebView theme text view.
case DomainsDatabaseHelper.DARK_THEME: // The domain WebView theme is dark.
// Set the icon according to the app theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_day));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_day, null));
} else {
- webViewThemeImageView.setImageDrawable(resources.getDrawable(R.drawable.webview_dark_theme_night));
+ webViewThemeImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.webview_dark_theme_night, null));
}
// Hide the WebView theme text view.
if (defaultWideViewport) { // Wide viewport is enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
}
} else { // Wide viewport is disabled by default.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_enabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_enabled_day, null));
}
// Hide the wide viewport text view.
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_night));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_night, null));
} else {
- wideViewportImageView.setImageDrawable(resources.getDrawable(R.drawable.wide_viewport_disabled_day));
+ wideViewportImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.wide_viewport_disabled_day, null));
}
// Hid ethe wide viewport text view.
if (defaultDisplayWebpageImages) { // Display webpage images is enabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
}
} else { // Display webpage images is disabled by default.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
}
}
case DomainsDatabaseHelper.ENABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_enabled_day, null));
}
// Hide the display images text view.
case DomainsDatabaseHelper.DISABLED:
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_night));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_night, null));
} else {
- displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_day));
+ displayWebpageImagesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.images_disabled_day, null));
}
// Hide the display images text view.
if (isChecked) { // SSL certificate pinning is enabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_night));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
} else {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_day));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
}
// Update the visibility of the saved SSL certificate.
} else { // SSL certificate pinning is disabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_night));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
} else {
- pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_day));
+ pinnedSslCertificateImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
}
// Hide the SSl certificates and instructions.
if (isChecked) { // IP addresses pinning is enabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_night));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_night, null));
} else {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_day));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_enabled_day, null));
}
// Update the visibility of the saved IP addresses card view.
} else { // IP addresses pinning is disabled.
// Set the icon according to the theme.
if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_night));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_night, null));
} else {
- pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_day));
+ pinnedIpAddressesImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ssl_certificate_disabled_day, null));
}
// Hide the IP addresses card views.
}
});
+ // Set the scroll Y.
+ domainSettingsScrollView.post(() -> domainSettingsScrollView.setScrollY(scrollY));
+
+ // Return the domain settings view.
return domainSettingsView;
}
import com.stoutner.privacybrowser.activities.DomainsActivity;
public class DomainsListFragment extends Fragment {
- // Instantiate the dismiss snackbar interface handle.
+ // Instantiate the dismiss snackbar interface.
private DismissSnackbarInterface dismissSnackbarInterface;
// Define the public dismiss snackbar interface.
// Run the default commands.
super.onAttach(context);
- // Get a handle for the dismiss snackbar interface.
+ // Populate the dismiss snackbar interface.
dismissSnackbarInterface = (DismissSnackbarInterface) context;
}
// Inflate `domains_list_fragment`. `false` does not attach it to the root `container`.
View domainsListFragmentView = inflater.inflate(R.layout.domains_list_fragment, container, false);
- // Initialize `domainsListView`.
+ // Get a handle for the domains listview.
ListView domainsListView = domainsListFragmentView.findViewById(R.id.domains_listview);
// Remove the incorrect lint error below that `.getSupportFragmentManager()` might be null.
DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
domainSettingsFragment.setArguments(argumentsBundle);
- // Display the domain settings fragment.
+ // Check to see if the device is in two paned mode.
if (DomainsActivity.twoPanedMode) { // The device in in two-paned mode.
// enable `deleteMenuItem` if the system is not waiting for a `Snackbar` to be dismissed.
if (!DomainsActivity.dismissingSnackbar) {
// Display the domain settings fragment.
supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit();
} else { // The device in in single-paned mode
+ // Save the domains listview position.
+ DomainsActivity.domainsListViewPosition = domainsListView.getFirstVisiblePosition();
+
// Show `deleteMenuItem` if the system is not waiting for a `Snackbar` to be dismissed.
if (!DomainsActivity.dismissingSnackbar) {
DomainsActivity.deleteMenuItem.setVisible(true);
}
});
+ // Return the domains list fragment.
return domainsListFragmentView;
}
}
\ No newline at end of file
package com.stoutner.privacybrowser.fragments;
-import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.LayoutInflater;
import com.stoutner.privacybrowser.R;
public class GuideTabFragment extends Fragment {
- // `tabNumber` is used in `onCreate()` and `onCreateView()`.
+ // Define the class variables.
private int tabNumber;
+ private View tabLayout;
// Store the tab number in the arguments bundle.
- public static GuideTabFragment createTab (int tab) {
+ public static GuideTabFragment createTab (int tabNumber) {
// Create a bundle.
Bundle bundle = new Bundle();
// Store the tab number in the bundle.
- bundle.putInt("Tab", tab);
+ bundle.putInt("tab_number", tabNumber);
- // Add the bundle to the fragment.
+ // Create a new guide tab fragment.
GuideTabFragment guideTabFragment = new GuideTabFragment();
+
+ // Add the bundle to the fragment.
guideTabFragment.setArguments(bundle);
// Return the new fragment.
// Run the default commands.
super.onCreate(savedInstanceState);
- // Remove the lint warning that `getArguments()` might be null.
- assert getArguments() != null;
+ // Get a handle for the arguments.
+ Bundle arguments = getArguments();
+
+ // Remove the lint warning below that arguments might be null.
+ assert arguments != null;
// Store the tab number in a class variable.
- tabNumber = getArguments().getInt("Tab");
+ tabNumber = arguments.getInt("tab_number");
}
- @SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- // Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically.
- View tabLayout = inflater.inflate(R.layout.bare_webview, container, false);
+ // Inflate the layout. The fragment will take care of attaching the root automatically.
+ tabLayout = inflater.inflate(R.layout.bare_webview, container, false);
// Get a handle for the tab WebView.
WebView tabWebView = (WebView) tabLayout;
}
}
+ // Scroll the WebView if the saved instance state is not null.
+ if (savedInstanceState != null) {
+ tabWebView.post(() -> tabWebView.setScrollY(savedInstanceState.getInt("scroll_y")));
+ }
+
// Return the formatted `tabLayout`.
return tabLayout;
}
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Get a handle for the tab WebView. A class variable cannot be used because it gets out of sync when restarting.
+ WebView tabWebView = (WebView) tabLayout;
+
+ // Save the scroll Y position if the tab WebView is not null, which can happen if a tab is not currently selected.
+ if (tabWebView != null) {
+ savedInstanceState.putInt("scroll_y", tabWebView.getScrollY());
+ }
+ }
}
\ No newline at end of file
/*
- * Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// The public interface is used to send information back to the parent activity.
public interface NewTabListener {
- void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url);
+ void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState);
}
// The new tab listener is used in `onAttach()` and `onCreateView()`.
private NewTabListener newTabListener;
+ // Define the bundle constants.
+ private final static String CREATE_NEW_PAGE = "create_new_page";
+ private final static String PAGE_NUMBER = "page_number";
+ private final static String URL = "url";
+ private final static String SAVED_STATE = "saved_state";
+ private final static String SAVED_NESTED_SCROLL_WEBVIEW_STATE = "saved_nested_scroll_webview_state";
+
+ // Define the class views.
+ NestedScrollWebView nestedScrollWebView;
+
@Override
public void onAttach(@NonNull Context context) {
// Run the default commands.
}
public static WebViewTabFragment createPage(int pageNumber, String url) {
- // Create a bundle.
- Bundle bundle = new Bundle();
+ // Create an arguments bundle.
+ Bundle argumentsBundle = new Bundle();
- // Store the page number and URL in the bundle.
- bundle.putInt("page_number", pageNumber);
- bundle.putString("url", url);
+ // Store the argument in the bundle.
+ argumentsBundle.putBoolean(CREATE_NEW_PAGE, true);
+ argumentsBundle.putInt(PAGE_NUMBER, pageNumber);
+ argumentsBundle.putString(URL, url);
// Create a new instance of the WebView tab fragment.
WebViewTabFragment webViewTabFragment = new WebViewTabFragment();
- // Add the bundle to the fragment.
- webViewTabFragment.setArguments(bundle);
+ // Add the arguments bundle to the fragment.
+ webViewTabFragment.setArguments(argumentsBundle);
// Return the new fragment.
return webViewTabFragment;
}
- @Override
- public View onCreateView(@NonNull LayoutInflater layoutInflater, ViewGroup container, Bundle savedInstanceState) {
- // Get the arguments.
- Bundle arguments = getArguments();
+ public static WebViewTabFragment restorePage(Bundle savedState, Bundle savedNestedScrollWebViewState) {
+ // Create an arguments bundle
+ Bundle argumentsBundle = new Bundle();
- // Remove the incorrect lint warning that the arguments might be null.
- assert arguments != null;
+ // Store the saved states in the arguments bundle.
+ argumentsBundle.putBundle(SAVED_STATE, savedState);
+ argumentsBundle.putBundle(SAVED_NESTED_SCROLL_WEBVIEW_STATE, savedNestedScrollWebViewState);
- // Get the variables from the arguments
- int pageNumber = arguments.getInt("page_number");
- String url = arguments.getString("url");
-
- // Inflate the tab's WebView. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically.
- View newPageView = layoutInflater.inflate(R.layout.webview_framelayout, container, false);
-
- // Get handles for the views.
- NestedScrollWebView nestedScrollWebView = newPageView.findViewById(R.id.nestedscroll_webview);
- ProgressBar progressBar = newPageView.findViewById(R.id.progress_bar);
+ // Create a new instance of the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = new WebViewTabFragment();
- // Store the WebView fragment ID in the nested scroll WebView.
- nestedScrollWebView.setWebViewFragmentId(fragmentId);
+ // Add the arguments bundle to the fragment.
+ webViewTabFragment.setArguments(argumentsBundle);
- // Request the main activity initialize the WebView.
- newTabListener.initializeWebView(nestedScrollWebView, pageNumber, progressBar, url);
+ // Return the new fragment.
+ return webViewTabFragment;
+ }
- // Return the new page view.
- return newPageView;
+ @Override
+ public View onCreateView(@NonNull LayoutInflater layoutInflater, ViewGroup container, Bundle savedInstanceState) {
+ // Check to see if the fragment is being restarted.
+ if (savedInstanceState == null) { // The fragment is not being restarted. Load and configure a new fragment.
+ // Get the arguments.
+ Bundle arguments = getArguments();
+
+ // Remove the incorrect lint warning that the arguments might be null.
+ assert arguments != null;
+
+ // Check to see if a new page is being created.
+ if (arguments.getBoolean(CREATE_NEW_PAGE)) { // A new page is being created.
+ // Get the variables from the arguments
+ int pageNumber = arguments.getInt(PAGE_NUMBER);
+ String url = arguments.getString(URL);
+
+ // Inflate the tab's WebView. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container.
+ // The fragment will take care of attaching the root automatically.
+ View newPageView = layoutInflater.inflate(R.layout.webview_framelayout, container, false);
+
+ // Get handles for the views.
+ nestedScrollWebView = newPageView.findViewById(R.id.nestedscroll_webview);
+ ProgressBar progressBar = newPageView.findViewById(R.id.progress_bar);
+
+ // Store the WebView fragment ID in the nested scroll WebView.
+ nestedScrollWebView.setWebViewFragmentId(fragmentId);
+
+ // Request the main activity initialize the WebView.
+ newTabListener.initializeWebView(nestedScrollWebView, pageNumber, progressBar, url, false);
+
+ // Return the new page view.
+ return newPageView;
+ } else { // A page is being restored.
+ // Get the saved states from the arguments.
+ Bundle savedState = arguments.getBundle(SAVED_STATE);
+ Bundle savedNestedScrollWebViewState = arguments.getBundle(SAVED_NESTED_SCROLL_WEBVIEW_STATE);
+
+ // Remove the incorrect lint warning below that the saved nested scroll WebView state might be null.
+ assert savedNestedScrollWebViewState != null;
+
+ // Inflate the tab's WebView. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container.
+ // The fragment will take care of attaching the root automatically.
+ View newPageView = layoutInflater.inflate(R.layout.webview_framelayout, container, false);
+
+ // Get handles for the views.
+ nestedScrollWebView = newPageView.findViewById(R.id.nestedscroll_webview);
+ ProgressBar progressBar = newPageView.findViewById(R.id.progress_bar);
+
+ // Store the WebView fragment ID in the nested scroll WebView.
+ nestedScrollWebView.setWebViewFragmentId(fragmentId);
+
+ // Restore the nested scroll WebView state.
+ nestedScrollWebView.restoreNestedScrollWebViewState(savedNestedScrollWebViewState);
+
+ // Restore the WebView state.
+ nestedScrollWebView.restoreState(savedState);
+
+ // Initialize the WebView.
+ newTabListener.initializeWebView(nestedScrollWebView, 0, progressBar, null, true);
+
+ // Return the new page view.
+ return newPageView;
+ }
+ } else { // The fragment is being restarted.
+ // Return null. Otherwise, the fragment will be inflated and initialized by the OS on a restart, discarded, and then recreated with saved settings by Privacy Browser.
+ return null;
+ }
}
}
\ No newline at end of file
/*
- * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// Extract the array of IDs not to get to the string builder.
for (long databaseIdLong : exceptIdLongArray) {
- if (idsNotToGetStringBuilder.length() == 0) { // This is the first number, so only add the number.
- idsNotToGetStringBuilder.append(databaseIdLong);
- } else { // This is not the first number, so place a `,` before the new number.
+ // Check to see if there is already a number in the builder.
+ if (idsNotToGetStringBuilder.length() > 0) {
+ // This is not the first number, so place a `,` before the new number.
idsNotToGetStringBuilder.append(",");
- idsNotToGetStringBuilder.append(databaseIdLong);
}
+
+ // Add the new number to the builder.
+ idsNotToGetStringBuilder.append(databaseIdLong);
}
// Prepare the SQL statement to select all items except those with the specified IDs.
// Extract the array of IDs not to get to the string builder.
for (long databaseIdLong : exceptIdLongArray) {
- if (idsNotToGetStringBuilder.length() == 0) { // This is the first number, so only add the number.
- idsNotToGetStringBuilder.append(databaseIdLong);
- } else { // This is not the first number, so place a `,` before the new number.
+ // Check to see if there is already a number in the builder.
+ if (idsNotToGetStringBuilder.length() > 0) {
+ // This is not the first number, so place a `,` before the new number.
idsNotToGetStringBuilder.append(",");
- idsNotToGetStringBuilder.append(databaseIdLong);
}
+
+ // Add the new number to the builder.
+ idsNotToGetStringBuilder.append(databaseIdLong);
}
// Prepare the SQL statement to select all items except those with the specified IDs.
// Extract the array of IDs not to get to the string builder.
for (long databaseIdLong : exceptIdLongArray) {
- if (idsNotToGetStringBuilder.length() == 0) { // This is the first number, so only add the number.
- idsNotToGetStringBuilder.append(databaseIdLong);
- } else { // This is not the first number, so place a `,` before the new number.
+ // Check to see if there is already a number in the builder.
+ if (idsNotToGetStringBuilder.length() > 0) {
+ // This is not the first number, so place a `,` before the new number.
idsNotToGetStringBuilder.append(",");
- idsNotToGetStringBuilder.append(databaseIdLong);
}
+
+ // Add the new number to the builder.
+ idsNotToGetStringBuilder.append(databaseIdLong);
}
// SQL escape the folder name.
// Extract the array of IDs not to get to the string builder.
for (long databaseIdLong : exceptIdLongArray) {
- if (idsNotToGetStringBuilder.length() == 0) { // This is the first number, so only add the number.
- idsNotToGetStringBuilder.append(databaseIdLong);
- } else { // This is not the first number, so place a `,` before the new number.
+ // Check to see if there is already a number in the builder.
+ if (idsNotToGetStringBuilder.length() > 0) {
+ // This is not the first number, so place a `,` before the new number.
idsNotToGetStringBuilder.append(",");
- idsNotToGetStringBuilder.append(databaseIdLong);
}
+
+ // Add the new number to the builder.
+ idsNotToGetStringBuilder.append(databaseIdLong);
}
// SQL escape `folderName`.
--- /dev/null
+/*
+ * Copyright © 2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.viewmodelfactories
+
+import androidx.annotation.Nullable
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+import java.net.Proxy
+import java.util.concurrent.ExecutorService
+
+class WebViewSourceFactory (@Nullable private val urlString: String, private val userAgent: String, private val doNotTrack: Boolean, private val localeString: String, private val proxy: Proxy,
+ private val executorService: ExecutorService): ViewModelProvider.Factory {
+ // Override the create function in order to add the provided arguments.
+ override fun <T: ViewModel?> create(modelClass: Class<T>): T {
+ // Return a new instance of the model class with the provided arguments.
+ return modelClass.getConstructor(String::class.java, String::class.java, Boolean::class.java, String::class.java, Proxy::class.java, ExecutorService::class.java)
+ .newInstance(urlString, userAgent, doNotTrack, localeString, proxy, executorService)
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.viewmodels;
+
+import android.text.SpannableStringBuilder;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.stoutner.privacybrowser.backgroundtasks.GetSourceBackgroundTask;
+
+import java.net.Proxy;
+import java.util.concurrent.ExecutorService;
+
+public class WebViewSource extends ViewModel {
+ // Initialize the mutable live data variables.
+ private final MutableLiveData<SpannableStringBuilder[]> mutableLiveDataSourceStringArray = new MutableLiveData<>();
+ private final MutableLiveData<String> mutableLiveDataErrorString = new MutableLiveData<>();
+
+ // Define the class variables.
+ private final String userAgent;
+ private final boolean doNotTrack;
+ private final String localeString;
+ private final Proxy proxy;
+ private final ExecutorService executorService;
+
+ // The public constructor.
+ public WebViewSource(@Nullable String urlString, String userAgent, boolean doNotTrack, String localeString, Proxy proxy, ExecutorService executorService) {
+ // Store the class variables.
+ this.userAgent = userAgent;
+ this.doNotTrack = doNotTrack;
+ this.localeString = localeString;
+ this.proxy = proxy;
+ this.executorService = executorService;
+
+ // Get the source.
+ updateSource(urlString);
+ }
+
+ // The source observer.
+ public LiveData<SpannableStringBuilder[]> observeSource() {
+ // Return the source to the activity.
+ return mutableLiveDataSourceStringArray;
+ }
+
+ // The error observer.
+ public LiveData<String> observeErrors() {
+ // Return any errors to the activity.
+ return mutableLiveDataErrorString;
+ }
+
+ // The interface for returning the error from the background task
+ public void returnError(String errorString) {
+ // Update the mutable live data error string.
+ mutableLiveDataErrorString.postValue(errorString);
+ }
+
+ // The workhorse that gets the source.
+ public void updateSource(String urlString) {
+ // Reset the mutable live data error string. This prevents the snackbar it from displaying a later if the activity restarts.
+ mutableLiveDataErrorString.postValue("");
+
+ // Instantiate the get source background task class.
+ GetSourceBackgroundTask getSourceBackgroundTask = new GetSourceBackgroundTask();
+
+ // Get the source.
+ executorService.execute(() -> mutableLiveDataSourceStringArray.postValue(getSourceBackgroundTask.acquire(urlString, userAgent, doNotTrack, localeString, proxy, this)));
+ }
+}
\ No newline at end of file
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.webkit.HttpAuthHandler;
// NestedScrollWebView extends WebView to handle nested scrolls (scrolling the app bar off the screen).
public class NestedScrollWebView extends WebView implements NestedScrollingChild2 {
- // These constants identify the blocklists.
+ // Define the blocklists constants.
public final static int BLOCKED_REQUESTS = 0;
public final static int EASYLIST = 1;
public final static int EASYPRIVACY = 2;
public final static int ULTRAPRIVACY = 6;
public final static int THIRD_PARTY_REQUESTS = 7;
+ // Define the saved state constants.
+ private final String DOMAIN_SETTINGS_APPLIED = "domain_settings_applied";
+ private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id";
+ private final String CURRENT_URl = "current_url";
+ private final String CURRENT_DOMAIN_NAME = "current_domain_name";
+ private final String ACCEPT_FIRST_PARTY_COOKIES = "accept_first_party_cookies";
+ private final String EASYLIST_ENABLED = "easylist_enabled";
+ private final String EASYPRIVACY_ENABLED = "easyprivacy_enabled";
+ private final String FANBOYS_ANNOYANCE_LIST_ENABLED = "fanboys_annoyance_list_enabled";
+ private final String FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED = "fanboys_social_blocking_list_enabled";
+ private final String ULTRALIST_ENABLED = "ultralist_enabled";
+ private final String ULTRAPRIVACY_ENABLED = "ultraprivacy_enabled";
+ private final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests";
+ private final String HAS_PINNED_SSL_CERTIFICATE = "has_pinned_ssl_certificate";
+ private final String PINNED_SSL_ISSUED_TO_CNAME = "pinned_ssl_issued_to_cname";
+ private final String PINNED_SSL_ISSUED_TO_ONAME = "pinned_ssl_issued_to_oname";
+ private final String PINNED_SSL_ISSUED_TO_UNAME = "pinned_ssl_issued_to_uname";
+ private final String PINNED_SSL_ISSUED_BY_CNAME = "pinned_ssl_issued_by_cname";
+ private final String PINNED_SSL_ISSUED_BY_ONAME = "pinned_ssl_issued_by_oname";
+ private final String PINNED_SSL_ISSUED_BY_UNAME = "pinned_ssl_issued_by_uname";
+ private final String PINNED_SSL_START_DATE = "pinned_ssl_start_date";
+ private final String PINNED_SSL_END_DATE = "pinned_ssl_end_date";
+ private final String HAS_PINNED_IP_ADDRESSES = "has_pinned_ip_addresses";
+ private final String PINNED_IP_ADDRESSES = "pinned_ip_addresses";
+ private final String IGNORE_PINNED_DOMAIN_INFORMATION = "ignore_pinned_domain_information";
+ private final String SWIPE_TO_REFRESH = "swipe_to_refresh";
+ private final String JAVASCRIPT_ENABLED = "javascript_enabled";
+ private final String DOM_STORAGE_ENABLED = "dom_storage_enabled";
+ private final String USER_AGENT = "user_agent";
+ private final String WIDE_VIEWPORT = "wide_viewport";
+ private final String FONT_SIZE = "font_size";
+
// Keep a copy of the WebView fragment ID.
private long webViewFragmentId;
// Keep track of when the domain name changes so that domain settings can be reapplied. This should never be null.
private String currentDomainName = "";
- // Track the status of first-party cookies.
+ // Track the status of first-party cookies. This is necessary because first-party cookie status is app wide instead of WebView specific.
private boolean acceptFirstPartyCookies;
// Track the resource requests.
return motionEventHandled;
}
+ public Bundle saveNestedScrollWebViewState() {
+ // Create a saved state bundle.
+ Bundle savedState = new Bundle();
+
+ // Initialize the long date variables.
+ long pinnedSslStartDateLong = 0;
+ long pinnedSslEndDateLong = 0;
+
+ // Convert the dates to longs.
+ if (pinnedSslStartDate != null) {
+ pinnedSslStartDateLong = pinnedSslStartDate.getTime();
+ }
+
+ if (pinnedSslEndDate != null) {
+ pinnedSslEndDateLong = pinnedSslEndDate.getTime();
+ }
+
+ // Populate the saved state bundle.
+ savedState.putBoolean(DOMAIN_SETTINGS_APPLIED, domainSettingsApplied);
+ savedState.putInt(DOMAIN_SETTINGS_DATABASE_ID, domainSettingsDatabaseId);
+ savedState.putString(CURRENT_URl, currentUrl);
+ savedState.putString(CURRENT_DOMAIN_NAME, currentDomainName);
+ savedState.putBoolean(ACCEPT_FIRST_PARTY_COOKIES, acceptFirstPartyCookies);
+ savedState.putBoolean(EASYLIST_ENABLED, easyListEnabled);
+ savedState.putBoolean(EASYPRIVACY_ENABLED, easyPrivacyEnabled);
+ savedState.putBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED, fanboysAnnoyanceListEnabled);
+ savedState.putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED, fanboysSocialBlockingListEnabled);
+ savedState.putBoolean(ULTRALIST_ENABLED, ultraListEnabled);
+ savedState.putBoolean(ULTRAPRIVACY_ENABLED, ultraPrivacyEnabled);
+ savedState.putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests);
+ savedState.putBoolean(HAS_PINNED_SSL_CERTIFICATE, hasPinnedSslCertificate);
+ savedState.putString(PINNED_SSL_ISSUED_TO_CNAME, pinnedSslIssuedToCName);
+ savedState.putString(PINNED_SSL_ISSUED_TO_ONAME, pinnedSslIssuedToOName);
+ savedState.putString(PINNED_SSL_ISSUED_TO_UNAME, pinnedSslIssuedToUName);
+ savedState.putString(PINNED_SSL_ISSUED_BY_CNAME, pinnedSslIssuedByCName);
+ savedState.putString(PINNED_SSL_ISSUED_BY_ONAME, pinnedSslIssuedByOName);
+ savedState.putString(PINNED_SSL_ISSUED_BY_UNAME, pinnedSslIssuedByUName);
+ savedState.putLong(PINNED_SSL_START_DATE, pinnedSslStartDateLong);
+ savedState.putLong(PINNED_SSL_END_DATE, pinnedSslEndDateLong);
+ savedState.putBoolean(HAS_PINNED_IP_ADDRESSES, hasPinnedIpAddresses);
+ savedState.putString(PINNED_IP_ADDRESSES, pinnedIpAddresses);
+ savedState.putBoolean(IGNORE_PINNED_DOMAIN_INFORMATION, ignorePinnedDomainInformation);
+ savedState.putBoolean(SWIPE_TO_REFRESH, swipeToRefresh);
+ savedState.putBoolean(JAVASCRIPT_ENABLED, this.getSettings().getJavaScriptEnabled());
+ savedState.putBoolean(DOM_STORAGE_ENABLED, this.getSettings().getDomStorageEnabled());
+ savedState.putString(USER_AGENT, this.getSettings().getUserAgentString());
+ savedState.putBoolean(WIDE_VIEWPORT, this.getSettings().getUseWideViewPort());
+ savedState.putInt(FONT_SIZE, this.getSettings().getTextZoom());
+
+ // Return the saved state bundle.
+ return savedState;
+ }
+
+ public void restoreNestedScrollWebViewState(Bundle savedState) {
+ // Restore the class variables.
+ domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED);
+ domainSettingsDatabaseId = savedState.getInt(DOMAIN_SETTINGS_DATABASE_ID);
+ currentUrl = savedState.getString(CURRENT_URl);
+ currentDomainName = savedState.getString(CURRENT_DOMAIN_NAME);
+ acceptFirstPartyCookies = savedState.getBoolean(ACCEPT_FIRST_PARTY_COOKIES);
+ easyListEnabled = savedState.getBoolean(EASYLIST_ENABLED);
+ easyPrivacyEnabled = savedState.getBoolean(EASYPRIVACY_ENABLED);
+ fanboysAnnoyanceListEnabled = savedState.getBoolean(FANBOYS_ANNOYANCE_LIST_ENABLED);
+ fanboysSocialBlockingListEnabled = savedState.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST_ENABLED);
+ ultraListEnabled = savedState.getBoolean(ULTRALIST_ENABLED);
+ ultraPrivacyEnabled = savedState.getBoolean(ULTRAPRIVACY_ENABLED);
+ blockAllThirdPartyRequests = savedState.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS);
+ hasPinnedSslCertificate = savedState.getBoolean(HAS_PINNED_SSL_CERTIFICATE);
+ pinnedSslIssuedToCName = savedState.getString(PINNED_SSL_ISSUED_TO_CNAME);
+ pinnedSslIssuedToOName = savedState.getString(PINNED_SSL_ISSUED_TO_ONAME);
+ pinnedSslIssuedToUName = savedState.getString(PINNED_SSL_ISSUED_TO_UNAME);
+ pinnedSslIssuedByCName = savedState.getString(PINNED_SSL_ISSUED_BY_CNAME);
+ pinnedSslIssuedByOName = savedState.getString(PINNED_SSL_ISSUED_BY_ONAME);
+ pinnedSslIssuedByUName = savedState.getString(PINNED_SSL_ISSUED_BY_UNAME);
+ hasPinnedIpAddresses = savedState.getBoolean(HAS_PINNED_IP_ADDRESSES);
+ pinnedIpAddresses = savedState.getString(PINNED_IP_ADDRESSES);
+ ignorePinnedDomainInformation = savedState.getBoolean(IGNORE_PINNED_DOMAIN_INFORMATION);
+ swipeToRefresh = savedState.getBoolean(SWIPE_TO_REFRESH);
+ this.getSettings().setJavaScriptEnabled(savedState.getBoolean(JAVASCRIPT_ENABLED));
+ this.getSettings().setDomStorageEnabled(savedState.getBoolean(DOM_STORAGE_ENABLED));
+ this.getSettings().setUserAgentString(savedState.getString(USER_AGENT));
+ this.getSettings().setUseWideViewPort(savedState.getBoolean(WIDE_VIEWPORT));
+ this.getSettings().setTextZoom(savedState.getInt(FONT_SIZE));
+
+ // Get the date longs.
+ long pinnedSslStartDateLong = savedState.getLong(PINNED_SSL_START_DATE);
+ long pinnedSslEndDateLong = savedState.getLong(PINNED_SSL_END_DATE);
+
+ // Set the pinned SSL start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslStartDateLong == 0) {
+ pinnedSslStartDate = null;
+ } else {
+ pinnedSslStartDate = new Date(pinnedSslStartDateLong);
+ }
+
+ // Set the Pinned SSL end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslEndDateLong == 0) {
+ pinnedSslEndDate = null;
+ } else {
+ pinnedSslEndDate = new Date(pinnedSslEndDateLong);
+ }
+ }
+
// The Android accessibility guidelines require overriding `performClick()` and calling it from `onTouchEvent()`.
@Override
public boolean performClick() {
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
-
- This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
- Privacy Browser is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Privacy Browser is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-
-<!-- Change the dark theme enabled text from white to gray. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <!-- `#4DFFFFFF` comes from the built-in `@color/primary_text_disabled_material_dark`. -->
- <item android:state_enabled="false" android:color="#4DFFFFFF" />
- <item android:color="@color/gray_300" />
-</selector>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
-
- This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
- Privacy Browser is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Privacy Browser is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-
-<!-- Change the dark theme enabled text from white to gray.-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:state_enabled="false" android:color="@color/gray_400" />
- <item android:color="@color/black" />
-</selector>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
-
- This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
- Privacy Browser is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Privacy Browser is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-
-<!-- Change the dark theme enabled text from white to gray. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:state_checked="false" android:color="@color/gray_500" />
- <item android:color="@color/gray_300" />
-</selector>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
-
- This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
- Privacy Browser is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Privacy Browser is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-
-<!-- Change the dark theme enabled text from white to gray.-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:state_checked="false" android:color="@color/gray_600" />
- <item android:color="@color/black" />
-</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2017,2019 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2017,2019-2020 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
You should have received a copy of the GNU General Public License
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-<!-- `android:choiceMode="singleChoice"` allows a selected domain to be highlighted.-->
+<!-- `android:choiceMode="singleChoice"` allows a selected domain to be highlighted.
+ `android:dividerHeight` must be at least `1dp` or the list view is inconsistent in calculating the scroll position when restarted.-->
<ListView
android:id="@+id/domains_listview"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:choiceMode="singleChoice"
android:divider="@color/transparent"
- android:dividerHeight="0dp" />
\ No newline at end of file
+ android:dividerHeight="1dp" />
\ No newline at end of file
You should have received a copy of the GNU General Public License
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-<ScrollView
- android:id="@+id/domain_settings_scrollview"
+<ScrollView android:id="@+id/domain_settings_scrollview"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
- android:descendantFocusability="beforeDescendants" >
+ android:descendantFocusability="beforeDescendants"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:layout_gravity="bottom"
android:src="@drawable/domains_day"
- android:tint="?attr/domainSettingsIconTintColor"
+ app:tint="?attr/domainSettingsIconTintColor"
tools:ignore="contentDescription" />
<!-- `TextInputLayout` makes the `android:hint` float above the `EditText`. -->
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/javascript_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/first_party_cookies_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/cookies_enabled"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/third_party_cookies_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/dom_storage_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/form_data_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/easylist_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/easyprivacy_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/fanboys_annoyance_list_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/fanboys_social_blocking_list_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/ultralist_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/ultraprivacy_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/block_all_third_party_requests_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:src="@drawable/user_agent_day"
- android:tint="?attr/domainSettingsIconTintColor"
+ app:tint="?attr/domainSettingsIconTintColor"
android:contentDescription="@string/user_agent" />
<Spinner
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:src="@drawable/font_size_day"
- android:tint="?attr/domainSettingsIconTintColor"
+ app:tint="?attr/domainSettingsIconTintColor"
android:contentDescription="@string/font_size" />
<Spinner
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/pinned_ssl_certificate_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginStart="8dp"
android:text="@string/pinned_ssl_certificate"
android:textColor="?android:textColorPrimary"
- android:textSize="18sp" />
+ android:textSize="18sp"
+ tools:ignore="TooManyViews" />
</LinearLayout>
<!-- Saved Certificate -->
android:layout_gravity="center_vertical"
tools:ignore="contentDescription" />
- <Switch
+ <androidx.appcompat.widget.SwitchCompat
android:id="@+id/pinned_ip_addresses_switch"
android:layout_height="wrap_content"
android:layout_width="match_parent"
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2017,2020 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
You should have received a copy of the GNU General Public License
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-<!-- `android:dividerHeight` must be specified with `android:divider` or the height will be `0dp`. In our case we want the height to be `0dp`. -->
+<!-- `android:dividerHeight` must be at least `1dp` or the list view is inconsistent in calculating the scroll position when restarted. -->
<ListView
android:id="@+id/domains_listview"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:divider="@color/transparent"
- android:dividerHeight="0dp" />
\ No newline at end of file
+ android:dividerHeight="1dp" />
\ No newline at end of file
android:layout_width="match_parent" >
<ScrollView
+ android:id="@+id/logcat_scrollview"
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2017-2018,2020 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
You should have received a copy of the GNU General Public License
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-<!-- A `CheckedTextView` allows the color of the text to be changed when it is selected (checked). -->
+<!-- A checked text view 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:paddingTop="8dp"
android:paddingBottom="8dp"
android:textSize="18sp"
- android:textColor="?attr/spinnerTextColorSelector" />
\ No newline at end of file
+ android:textColor="@color/checked_text_color_selector" />
\ No newline at end of file
android:layout_width="match_parent">
<ScrollView
+ android:id="@+id/view_source_scrollview"
android:layout_height="wrap_content"
android:layout_width="match_parent" >
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <!-- The nested scroll WebView is created with `visibility="invisible"` to prevent a white background splash in night mode. It is set visible in `onProgressChanged()` in `MainWebViewActivity`. -->
+ <!-- The nested scroll WebView is created with `visibility="invisible"` to prevent a white background splash in night mode because there is a delay in setting the background color.
+ It is set visible in `initializeWebView()` or `onProgressChanged()` in `MainWebViewActivity`. -->
<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:background="?android:attr/colorBackground"
android:visibility="invisible" />
<!-- `android:max` changes the maximum `ProgressBar` value from 10000 to 100 to match progress percentage.
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<!-- `android:iconTint` can be used once the minimum API >= 26 instead of including a separate drawable for each theme. -->
<item
- android:id="@+id/options_menu_sort"
+ android:id="@+id/sort"
android:title="@string/sort"
android:orderInCategory="10"
android:icon="?attr/sortIcon"
<item>Herunterziehen zum aktualisieren aktiviert</item>
<item>Herunterziehen zum aktualisieren deaktiviert</item>
</string-array>
+ <string-array name="webview_theme_array">
+ <item>System-Einstellung</item>
+ <item>Helles WebView-Erscheinungsbild</item>
+ <item>Dunkles WebView-Erscheinungsbild</item>
+ </string-array>
<string-array name="wide_viewport_array">
<item>Standardeinstellung</item>
<item>Breiter Anzeigebereich aktiviert</item>
<item>Hell</item>
<item>Dunkel</item>
</string-array>
+ <string name="webview_theme">WebView-Erscheinungsbild</string>
+ <string-array name="webview_theme_entries">
+ <item>System-Einstellung</item>
+ <item>Hell</item>
+ <item>Dunkel</item>
+ </string-array>
<string name="wide_viewport_preference">Breiter Anzeigebereich</string>
<string name="wide_viewport_summary">Wird der breite Anzeigebereich verwendet, werden manche Webseiten eher wie am Desktop (Standrechner, Laptop) angezeigt.</string>
<string name="display_webpage_images">Webseiten-Bilder anzeigen</string>
<item>Deslizar para actualizar habilitado</item>
<item>Deslizar para actualizar deshabilitado</item>
</string-array>
+ <string-array name="webview_theme_array">
+ <item>Por defecto del sistema</item>
+ <item>Tema de WebView claro</item>
+ <item>Tema de WebView oscuro</item>
+ </string-array>
<string-array name="wide_viewport_array">
<item>Por defecto del sistema</item>
<item>Vista amplia habilitada</item>
<item>Claro</item>
<item>Oscuro</item>
</string-array>
+ <string name="webview_theme">Tema de WebView</string>
+ <string-array name="webview_theme_entries">
+ <item>Por defecto del sistema</item>
+ <item>Claro</item>
+ <item>Oscuro</item>
+ </string-array>
<string name="wide_viewport_preference">Vista amplia</string>
<string name="wide_viewport_summary">El uso de una vista amplia hace que el diseño de algunas páginas web se parezca más al sitio de escritorio.</string>
<string name="display_webpage_images">Mostrar imágenes de la página web</string>
<string name="domain_deleted">Domaine supprimé</string>
<string name="domain_name_instructions">Faire précéder par *. pour inclure l\'ensemble des sous-domaines (ex. *.stoutner.com)</string>
<string-array name="font_size_array">
- <item>Réglages systèmes</item>
+ <item>Réglages système</item>
<item>Taille de police personnalisée</item>
</string-array>
<string-array name="swipe_to_refresh_array">
- <item>Réglages systèmes</item>
+ <item>Réglages système</item>
<item>Glisser pour actualiser activé</item>
<item>Glisser pour actualiser déactivé</item>
</string-array>
+ <string-array name="webview_theme_array">
+ <item>Réglages système</item>
+ <item>Thème WebView clair</item>
+ <item>Thème WebView sombre</item>
+ </string-array>
<string-array name="wide_viewport_array">
- <item>Réglages systèmes</item>
+ <item>Réglages système</item>
<item>Mode fenêtre large activé</item>
<item>Mode fenêtre largé désactivé</item>
</string-array>
<string-array name="display_webpage_images_array">
- <item>Réglages systèmes</item>
+ <item>Réglages système</item>
<item>Images affichées</item>
<item>Images masquées</item>
</string-array>
pour activer/désactiver les cookies et le stockage DOM.</string>
<string name="app_theme">Thème de l\'application</string>
<string-array name="app_theme_entries">
- <item>Par defaut</item>
+ <item>Par défaut</item>
+ <item>Clair</item>
+ <item>Sombre</item>
+ </string-array>
+ <string name="webview_theme">Thème Android WebView</string>
+ <string-array name="webview_theme_entries">
+ <item>Par défaut</item>
<item>Clair</item>
<item>Sombre</item>
</string-array>
<item>Swipe per aggiornare abilitato</item>
<item>Swipe per aggiornare disabilitato</item>
</string-array>
+ <string-array name="webview_theme_array">
+ <item>Predefinito</item>
+ <item>Tema Chiaro</item>
+ <item>Tema Scuro</item>
+ </string-array>
<string-array name="wide_viewport_array">
<item>Impostazioni di default</item>
<item>Finestra grande abilitata</item>
<item>Chiaro</item>
<item>Scuro</item>
</string-array>
+ <string name="webview_theme">Tema di WebView</string>
+ <string-array name="webview_theme_entries">
+ <item>Predefinito</item>
+ <item>Chiaro</item>
+ <item>Scuro</item>
+ </string-array>
<string name="wide_viewport_preference">Finestra grande</string>
<string name="wide_viewport_summary">L\'utilizzo di una finestra grande permette la visualizzazione di alcune pagine web come in modalità desktop.</string>
<string name="display_webpage_images">Mostra immagini delle pagine web</string>
<item>Потянуть для обновления - включено</item>
<item>Потянуть для обновления - выключено</item>
</string-array>
+ <string-array name="webview_theme_array">
+ <item>Настройки по умолчанию</item>
+ <item>Светлая тема WebView</item>
+ <item>Темная тема WebView</item>
+ </string-array>
<string-array name="wide_viewport_array">
<item>Настройки по умолчанию</item>
<item>Широкий вид просмотра включен</item>
<string name="scroll_app_bar_summary">Прокручивает панель приложения вверху экрана при прокрутке WebView вниз.</string>
<string name="display_additional_app_bar_icons">Отображать дополнительные значки на панели приложения</string>
<string name="display_additional_app_bar_icons_summary">Отображать значки на панели приложения для обновления WebView и, при наличии места, для переключения файлов cookie и хранилища DOM</string>
- <string name="app_theme">Тема</string>
+ <string name="app_theme">Тема приложения</string>
<string-array name="app_theme_entries">
<item>По умолчанию</item>
<item>Светлая</item>
<item>Темная</item>
</string-array>
+ <string name="webview_theme">Тема WebView</string>
+ <string-array name="webview_theme_entries">
+ <item>По умолчанию</item>
+ <item>Светлая</item>
+ <item>Темная</item>
+ </string-array>
<string name="wide_viewport_preference">Широкий вид просмотра</string>
<string name="wide_viewport_summary">Использование широкого вида просмотра делает стиль некоторых веб-страниц более похожим на сайт для компьютера.</string>
<string name="display_webpage_images">Показывать изображения веб-страницы</string>
<!-- Tints. -->
<item name="addTabIconTintColor">@color/gray_700</item>
- <item name="domainSettingsIconTintColor">@color/blue_700</item>
+ <item name="domainSettingsIconTintColor">@color/blue_800</item>
<item name="fabIconTintColor">@color/white</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
<item name="navigationIconTintColor">@color/blue_800</item>
<!-- Tints. -->
<item name="addTabIconTintColor">@color/gray_700</item>
- <item name="domainSettingsIconTintColor">@color/blue_700</item>
+ <item name="domainSettingsIconTintColor">@color/blue_800</item>
<item name="fabIconTintColor">@color/white</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
<item name="navigationIconTintColor">@color/blue_800</item>
<attr name="buttonBackgroundColorSelector" format="color" />
<attr name="buttonTextColorSelector" format="color" />
<attr name="redTextColor" format="color" />
- <attr name="spinnerTextColorSelector" format="color" />
<!-- Tint Colors. -->
<attr name="addTabIconTintColor" format="color" />
<!-- Tints. -->
<item name="addTabIconTintColor">@color/gray_700</item>
- <item name="domainSettingsIconTintColor">@color/blue_700</item>
+ <item name="domainSettingsIconTintColor">@color/blue_800</item>
<item name="fabIconTintColor">@color/white</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
<item name="navigationIconTintColor">@color/blue_800</item>
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.0'
+ classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
// NOTE: Do not place your application dependencies here; they belong