// Scroll to where the deleted bookmark was located.
bookmarksListView.setSelection(selectedBookmarkPosition - 5);
+ // Initialize `snackbarMessage`.
String snackbarMessage;
// Determine how many items are in the array and prepare an appropriate Snackbar message.
.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
- // Android Studio wants to see entries for every possible `Snackbar.Callback` even if they aren't used.
switch (event) {
- // The user pushed the "Undo" button.
+ // The user pushed the `Undo` button.
case Snackbar.Callback.DISMISS_EVENT_ACTION:
// Refresh the ListView to show the rows again.
updateBookmarksListView(currentFolder);
// Scroll to where the deleted bookmark was located.
bookmarksListView.setSelection(selectedBookmarkPosition - 5);
-
break;
- case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
- // Do nothing and let the default behavior run.
-
- case Snackbar.Callback.DISMISS_EVENT_MANUAL:
- // Do nothing and let the default behavior run.
-
- case Snackbar.Callback.DISMISS_EVENT_SWIPE:
- // Do nothing and let the default behavior run.
-
- case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
- // Do nothing and let the default behavior run.
-
- // The Snackbar was dismissed without the "Undo" button being pushed.
+ // The `Snackbar` was dismissed without the `Undo` button being pushed.
default:
// Delete each selected row.
for (long databaseIdLong : selectedBookmarksLongArray) {
package com.stoutner.privacybrowser.activities;
+import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
// Initialize the database handler. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`.
// The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
- DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getApplicationContext(), null, null, 0);
+ final DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getApplicationContext(), null, null, 0);
switch (menuItemID) {
case android.R.id.home: // The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
break;
case R.id.delete_domain:
- // Delete the selected domain.
- domainsDatabaseHelper.deleteDomain(databaseId);
-
- // Navigate to `DomainsActivity`.
- NavUtils.navigateUpFromSameTask(this);
+ // Get a handle for the current activity.
+ final Activity activity = this;
+
+ // Get a handle for `domain_settings_coordinatorlayout` so we can display a `SnackBar` later.
+ CoordinatorLayout domainSettingsCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.domain_settings_coordinatorlayout);
+
+ // Detach `domain_settings_scrollview`.
+ getSupportFragmentManager().beginTransaction().detach(getSupportFragmentManager().findFragmentById(R.id.domain_settings_scrollview)).commit();
+
+ Snackbar.make(domainSettingsCoordinatorLayout, R.string.domain_deleted, Snackbar.LENGTH_SHORT)
+ .setAction(R.string.undo, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Do nothing because everything will be handled by `onDismiss` below.
+ }
+ })
+ .addCallback(new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ switch (event) {
+ // The user pushed the `undo` button.
+ case Snackbar.Callback.DISMISS_EVENT_ACTION:
+ // Store `databaseId` in `argumentsBundle`.
+ Bundle argumentsBundle = new Bundle();
+ argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseId);
+
+ // Add `argumentsBundle` to `domainSettingsFragment`.
+ DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+ domainSettingsFragment.setArguments(argumentsBundle);
+
+ // Display `domainSettingsFragment`.
+ getSupportFragmentManager().beginTransaction().replace(R.id.domain_settings_scrollview, domainSettingsFragment).commit();
+ break;
+
+ // The `Snackbar` was dismissed without the `Undo` button being pushed.
+ default:
+ // Delete the selected domain.
+ domainsDatabaseHelper.deleteDomain(databaseId);
+
+ // Navigate to `DomainsActivity`.
+ NavUtils.navigateUpFromSameTask(activity);
+ break;
+ }
+ }
+ })
+ .show();
break;
}
return true;
import android.database.Cursor;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener {
// `context` is used in `onCreate()` and `onOptionsItemSelected()`.
- Context context;
+ private Context context;
// `domainsDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onAddDomain()`, and `updateDomainsRecyclerView()`.
private static DomainsDatabaseHelper domainsDatabaseHelper;
// `twoPaneMode` is used in `onCreate()` and `updateDomainsListView()`.
private boolean twoPaneMode;
- // `domainsRecyclerView` is used in `onCreate()` and `updateDomainsListView()`.
+ // `domainsListView` is used in `onCreate()`, `onOptionsItemSelected()`, and `updateDomainsListView()`.
private ListView domainsListView;
// `databaseId` is used in `onCreate()` and `onOptionsItemSelected()`.
// Display `domainSettingsFragment`.
getSupportFragmentManager().beginTransaction().replace(R.id.domain_settings_scrollview, domainSettingsFragment).commit();
+
+ // Enable the options `MenuItems`.
+ deleteMenuItem.setEnabled(true);
+ deleteMenuItem.setIcon(R.drawable.delete);
+ saveMenuItem.setEnabled(true);
} else { // Load the second activity on smaller screens.
// Create `domainSettingsActivityIntent` with the `databaseId`.
Intent domainSettingsActivityIntent = new Intent(context, DomainSettingsActivity.class);
// Save the domain settings.
domainsDatabaseHelper.saveDomain(databaseId, domainNameString, javaScriptEnabled, firstPartyCookiesEnabled, thirdPartyCookiesEnabled, domStorageEnabledEnabled, formDataEnabled, userAgentString, fontSizeInt);
+
+ // Display a `Snackbar`.
+ Snackbar.make(domainsListView, R.string.domain_settings_saved, Snackbar.LENGTH_SHORT).show();
+
+ // update the domains `ListView`.
+ updateDomainsListView();
break;
case R.id.delete_domain:
- // Delete the selected domain.
- domainsDatabaseHelper.deleteDomain(databaseId);
+ // Save the `ListView` `currentPosition`.
+ final int currentPosition = domainsListView.getCheckedItemPosition();
+
+ // Get a `Cursor` that does not show the domain to be deleted.
+ Cursor domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseId);
+
+ // Setup `domainsPendingDeleteCursorAdapter` with `this` context. `false` disables `autoRequery`.
+ CursorAdapter domainsPendingDeleteCursorAdapter = new CursorAdapter(this, domainsPendingDeleteCursor, false) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ // Inflate the individual item layout. `false` does not attach it to the root.
+ return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Set the domain name.
+ String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
+ TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview);
+ domainNameTextView.setText(domainNameString);
+ }
+ };
+
+ // Update the `ListView`.
+ domainsListView.setAdapter(domainsPendingDeleteCursorAdapter);
- // Detach the domain settings fragment.
+ // Detach the domain settings `Fragment`.
getSupportFragmentManager().beginTransaction().detach(getSupportFragmentManager().findFragmentById(R.id.domain_settings_scrollview)).commit();
- // Update the `ListView`.
- updateDomainsListView();
+ // Disable the options `MenuItems`.
+ deleteMenuItem.setEnabled(false);
+ deleteMenuItem.setIcon(R.drawable.delete_blue);
+ saveMenuItem.setEnabled(false);
+
+ // Display a `Snackbar`.
+ Snackbar.make(domainsListView, R.string.domain_deleted, Snackbar.LENGTH_LONG)
+ .setAction(R.string.undo, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Do nothing because everything will be handled by `onDismissed()` below.
+ }
+ })
+ .addCallback(new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ switch (event) {
+ // The user pushed the `Undo` button.
+ case Snackbar.Callback.DISMISS_EVENT_ACTION:
+ // Get a `Cursor` with the current contents of the domains database.
+ Cursor undoDeleteDomainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
+
+ // Setup `domainsCursorAdapter` with `this` context. `false` disables `autoRequery`.
+ CursorAdapter undoDeleteDomainsCursorAdapter = new CursorAdapter(context, undoDeleteDomainsCursor, false) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ // Inflate the individual item layout. `false` does not attach it to the root.
+ return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Set the domain name.
+ String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
+ TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview);
+ domainNameTextView.setText(domainNameString);
+ }
+ };
+
+ // Update the `ListView`.
+ domainsListView.setAdapter(undoDeleteDomainsCursorAdapter);
+
+ // Select the entry in the domain list at `currentPosition`.
+ domainsListView.setItemChecked(currentPosition, true);
+
+ // Store `databaseId` in `argumentsBundle`.
+ Bundle argumentsBundle = new Bundle();
+ argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseId);
+
+ // Add `argumentsBundle` to `domainSettingsFragment`.
+ DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+ domainSettingsFragment.setArguments(argumentsBundle);
+
+ // Display `domainSettingsFragment`.
+ getSupportFragmentManager().beginTransaction().replace(R.id.domain_settings_scrollview, domainSettingsFragment).commit();
+
+ // Enable the options `MenuItems`.
+ deleteMenuItem.setEnabled(true);
+ deleteMenuItem.setIcon(R.drawable.delete);
+ saveMenuItem.setEnabled(true);
+ break;
+
+ // The `Snackbar` was dismissed without the `Undo` button being pushed.
+ default:
+ // Delete the selected domain.
+ domainsDatabaseHelper.deleteDomain(databaseId);
+ break;
+ }
+ }
+ })
+ .show();
break;
}
+
+ // Consume the event.
return true;
}
}
private void updateDomainsListView() {
+ // Initialize `currentPosition`.
+ int currentPosition;
+
+ // Store the current position of `domainsListView` if it is already populated.
+ if (domainsListView.getCount() > 0){
+ currentPosition = domainsListView.getCheckedItemPosition();
+ } else {
+ // Set `currentPosition` to 0;
+ currentPosition = 0;
+ }
+
// Get a `Cursor` with the current contents of the domains database.
- Cursor domainsCursor = domainsDatabaseHelper.getCursorOrderedByDomain();
+ Cursor domainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
// Setup `domainsCursorAdapter` with `this` context. `false` disables `autoRequery`.
CursorAdapter domainsCursorAdapter = new CursorAdapter(this, domainsCursor, false) {
// Display the domain settings in the second pane if operating in two pane mode and the database contains at least one domain.
if (twoPaneMode && (domainsCursor.getCount() > 0)) {
- // Select the first domain.
- domainsListView.setItemChecked(0, true);
+ // Select the entry in the domain list at `currentPosition`.
+ domainsListView.setItemChecked(currentPosition, true);
- // Get the `databaseId` of the first item.
- domainsCursor.moveToFirst();
+ // Get the `databaseId` for `currentPosition`.
+ domainsCursor.moveToPosition(currentPosition);
databaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
// Store `databaseId` in `argumentsBundle`.
MenuItem toggleDomStorage = menu.findItem(R.id.toggleDomStorage);
MenuItem toggleSaveFormData = menu.findItem(R.id.toggleSaveFormData);
- // Only display third-Party Cookies if SDK >= 21
+ // Only display third-party cookies if SDK >= 21
toggleThirdPartyCookies.setVisible(Build.VERSION.SDK_INT >= 21);
// Get the shared preference values. `this` references the current context.
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
+import android.os.Build;
import android.os.Bundle;
// We have to use `android.support.v4.app.Fragment` until minimum API >= 23. Otherwise we cannot call `getContext()`.
import android.support.v4.app.Fragment;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
final ImageView javaScriptImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_javascript_imageview);
Switch firstPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_switch);
final ImageView firstPartyCookiesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_imageview);
- Switch thirdPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_switch);
+ LinearLayout thirdPartyCookiesLinearLayout = (LinearLayout) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_linearlayout);
+ final Switch thirdPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_switch);
final ImageView thirdPartyCookiesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_imageview);
- Switch domStorageEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_switch);
+ final Switch domStorageEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_switch);
final ImageView domStorageImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_imageview);
Switch formDataEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_form_data_switch);
final ImageView formDataImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_form_data_imageview);
javaScriptImageView.setImageDrawable(getResources().getDrawable(R.drawable.privacy_mode));
}
- // Set the first-party cookies status.
+ // Set the first-party cookies status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
if (firstPartyCookiesEnabledInt == 1) { // First-party cookies are enabled.
firstPartyCookiesEnabledSwitch.setChecked(true);
- firstPartyCookiesImageView.setEnabled(true);
+ firstPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_enabled));
} else { // First-party cookies are disabled.
firstPartyCookiesEnabledSwitch.setChecked(false);
- firstPartyCookiesImageView.setEnabled(false);
+ firstPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_disabled));
}
- // Set the third-party cookies status.
- if (thirdPartyCookiesEnabledInt == 1) { // Third-party cookies are enabled.
- thirdPartyCookiesEnabledSwitch.setChecked(true);
- thirdPartyCookiesImageView.setEnabled(true);
- } else { // Third-party cookies are disabled.
- thirdPartyCookiesEnabledSwitch.setChecked(false);
- thirdPartyCookiesImageView.setEnabled(false);
+ // Only display third-party cookies if SDK_INT >= 21.
+ if (Build.VERSION.SDK_INT >= 21) { // Third-party cookies can be configured for API >= 21.
+ // Only enable third-party-cookies if first-party cookies are enabled.
+ if (firstPartyCookiesEnabledInt == 1) { // First-party cookies are enabled.
+ // Set the third-party cookies status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
+ if (thirdPartyCookiesEnabledInt == 1) { // Both first-party and third-party cookies are enabled.
+ thirdPartyCookiesEnabledSwitch.setChecked(true);
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_warning));
+ } else { // First party cookies are enabled but third-party cookies are disabled.
+ thirdPartyCookiesEnabledSwitch.setChecked(false);
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_disabled));
+ }
+ } else { // First-party cookies are disabled.
+ // Set the status of third-party cookies, but disable it.
+ if (thirdPartyCookiesEnabledInt == 1) { // Third-party cookies are enabled but first-party cookies are disabled.
+ thirdPartyCookiesEnabledSwitch.setChecked(true);
+ thirdPartyCookiesEnabledSwitch.setEnabled(false);
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_ghosted));
+ } else { // Both first party and third-party cookies are disabled.
+ thirdPartyCookiesEnabledSwitch.setChecked(false);
+ thirdPartyCookiesEnabledSwitch.setEnabled(false);
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_ghosted));
+ }
+ }
+ } else { // Third-party cookies cannot be configured for API <= 21.
+ // Hide the `LinearLayout` for third-party cookies.
+ thirdPartyCookiesLinearLayout.setVisibility(View.GONE);
}
- // Set the DOM storage status.
- if (domStorageEnabledInt == 1) { // DOM storage is enabled.
- domStorageEnabledSwitch.setChecked(true);
- domStorageImageView.setEnabled(true);
- } else { // Dom storage is disabled.
- domStorageEnabledSwitch.setChecked(false);
- domStorageImageView.setEnabled(false);
+ // Only enable DOM storage if JavaScript is enabled.
+ if (javaScriptEnabledInt == 1) { // JavaScript is enabled.
+ // Set the DOM storage status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
+ if (domStorageEnabledInt == 1) { // Both JavaScript and DOM storage are enabled.
+ domStorageEnabledSwitch.setChecked(true);
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_enabled));
+ } else { // JavaScript is enabled but DOM storage is disabled.
+ domStorageEnabledSwitch.setChecked(false);
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_disabled));
+ }
+ } else { // JavaScript is disabled.
+ // Set the status of DOM storage, but disable it.
+ if (domStorageEnabledInt == 1) { // DOM storage is enabled but JavaScript is disabled.
+ domStorageEnabledSwitch.setChecked(true);
+ domStorageEnabledSwitch.setEnabled(false);
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_ghosted));
+ } else { // Both JavaScript and DOM storae are disabled.
+ domStorageEnabledSwitch.setChecked(false);
+ domStorageEnabledSwitch.setEnabled(false);
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_ghosted));
+ }
}
- // Set the form data status.
+ // Set the form data status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons.
if (formDataEnabledInt == 1) { // Form data is enabled.
formDataEnabledSwitch.setChecked(true);
- formDataImageView.setEnabled(true);
+ formDataImageView.setImageDrawable(getResources().getDrawable(R.drawable.form_data_enabled));
} else { // Form data is disabled.
formDataEnabledSwitch.setChecked(false);
- formDataImageView.setEnabled(false);
+ formDataImageView.setImageDrawable(getResources().getDrawable(R.drawable.form_data_disabled));
}
// We need to inflated a `WebView` to get the default user agent.
javaScriptEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- // Update the icon.
- if (isChecked) {
+ if (isChecked) { // JavaScript is enabled.
+ // Update the JavaScript icon.
javaScriptImageView.setImageDrawable(getResources().getDrawable(R.drawable.javascript_enabled));
- } else {
+
+ // Enable the DOM storage `Switch`.
+ domStorageEnabledSwitch.setEnabled(true);
+
+ // Update the DOM storage icon.
+ if (domStorageEnabledSwitch.isChecked()) { // DOM storage is enabled.
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_enabled));
+ } else { // DOM storage is disabled.
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_disabled));
+ }
+ } else { // JavaScript is disabled.
+ // Update the JavaScript icon.
javaScriptImageView.setImageDrawable(getResources().getDrawable(R.drawable.privacy_mode));
+
+ // Disable the DOM storage `Switch`.
+ domStorageEnabledSwitch.setEnabled(false);
+
+ // Set the DOM storage icon to be ghosted.
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_ghosted));
}
}
});
firstPartyCookiesEnabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- // Update the icon.
- firstPartyCookiesImageView.setEnabled(isChecked);
+ if (isChecked) { // First-party cookies are enabled.
+ // Update the first-party cookies icon.
+ firstPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_enabled));
+
+ // Enable the third-party cookies `Switch`.
+ thirdPartyCookiesEnabledSwitch.setEnabled(true);
+
+ // Update the third-party cookies icon.
+ if (thirdPartyCookiesEnabledSwitch.isChecked()) { // Third-party cookies are enabled.
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_warning));
+ } else { // Third-party cookies are disabled.
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_disabled));
+ }
+ } else { // First-party cookies are disabled.
+ // Update the first-party cookies icon.
+ firstPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_disabled));
+
+ // Disable the third-party cookies `Switch`.
+ thirdPartyCookiesEnabledSwitch.setEnabled(false);
+
+ // Set the third-party cookies icon to be ghosted.
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_ghosted));
+ }
}
});
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Update the icon.
- thirdPartyCookiesImageView.setEnabled(isChecked);
+ if (isChecked) {
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_warning));
+ } else {
+ thirdPartyCookiesImageView.setImageDrawable(getResources().getDrawable(R.drawable.cookies_disabled));
+ }
}
});
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Update the icon.
- domStorageImageView.setEnabled(isChecked);
+ if (isChecked) {
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_enabled));
+ } else {
+ domStorageImageView.setImageDrawable(getResources().getDrawable(R.drawable.dom_storage_disabled));
+ }
}
});
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Update the icon.
- formDataImageView.setEnabled(isChecked);
+ if (isChecked) {
+ formDataImageView.setImageDrawable(getResources().getDrawable(R.drawable.form_data_enabled));
+ } else {
+ formDataImageView.setImageDrawable(getResources().getDrawable(R.drawable.form_data_disabled));
+ }
}
});
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
// Prepare the SQL statement to get the `Cursor` for `databaseId`
- final String GET_ONE_BOOKMARK = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_ONE_BOOKMARK = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + _ID + " = " + databaseId;
// Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. We can't close the `Cursor` because we need to use it in the parent activity.
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
// Prepare the SQL statement to get the `Cursor` for the folder.
- final String GET_FOLDER = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + _ID + " = " + databaseId;
// Get `folderCursor`. The second argument is `null` because there are no `selectionArgs`.
folderName = DatabaseUtils.sqlEscapeString(folderName);
// Prepare the SQL statement to get the `Cursor` for the folder.
- final String GET_FOLDER = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + BOOKMARK_NAME + " = " + folderName +
" AND " + IS_FOLDER + " = " + 1;
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
// Prepare the SQL statement to get the `Cursor` for the folders.
- final String GET_FOLDERS_EXCEPT = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_FOLDERS_EXCEPT = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + IS_FOLDER + " = " + 1 +
" AND " + BOOKMARK_NAME + " NOT IN (" + exceptFolders +
") ORDER BY " + BOOKMARK_NAME + " ASC";
currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
// Prepare the SQL statement to get the `Cursor` for the subfolders.
- final String GET_SUBFOLDERS = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_SUBFOLDERS = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + PARENT_FOLDER + " = " + currentFolder +
" AND " + IS_FOLDER + " = " + 1;
currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
// Prepare the SQL statement to get the parent folder.
- final String GET_PARENT_FOLDER = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_PARENT_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + IS_FOLDER + " = " + 1 +
" AND " + BOOKMARK_NAME + " = " + currentFolder;
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
// Get everything in `BOOKMARKS_TABLE`.
- final String GET_ALL_BOOKMARKS = "Select * FROM " + BOOKMARKS_TABLE;
+ final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE;
// Return the results as a Cursor. The second argument is `null` because there are no selectionArgs.
// We can't close the Cursor because we need to use it in the parent activity.
folderName = DatabaseUtils.sqlEscapeString(folderName);
// Get everything in the `BOOKMARKS_TABLE` with `folderName` as the `PARENT_FOLDER`.
- final String GET_ALL_BOOKMARKS = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + PARENT_FOLDER + " = " + folderName +
" ORDER BY " + DISPLAY_ORDER + " ASC";
folderName = DatabaseUtils.sqlEscapeString(folderName);
// Prepare the SQL statement to select all items except those with the specified IDs.
- final String GET_All_BOOKMARKS_EXCEPT_SPECIFIED = "Select * FROM " + BOOKMARKS_TABLE +
+ final String GET_All_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + PARENT_FOLDER + " = " + folderName +
" AND " + _ID + " NOT IN (" + doNotGetIdsString +
") ORDER BY " + DISPLAY_ORDER + " ASC";
SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
// Prepare the SQL statement to determine if `databaseId` is a folder.
- final String CHECK_IF_FOLDER = "Select * FROM " + BOOKMARKS_TABLE +
+ final String CHECK_IF_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + _ID + " = " + databaseId;
// Populate folderCursor. The second argument is `null` because there are no `selectionArgs`.
// Get the highest `DISPLAY_ORDER` in the new folder
String newFolderSqlEscaped = DatabaseUtils.sqlEscapeString(newFolder);
- final String NEW_FOLDER = "Select * FROM " + BOOKMARKS_TABLE +
+ final String NEW_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
" WHERE " + PARENT_FOLDER + " = " + newFolderSqlEscaped +
" ORDER BY " + DISPLAY_ORDER + " ASC";
// The second argument is `null` because there are no `selectionArgs`.
// Code for upgrading the database will be added here when the schema version > 1.
}
- public Cursor getCursorOrderedByDomain() {
+ public Cursor getDomainNameCursorOrderedByDomain() {
// Get a readable database handle.
SQLiteDatabase domainsDatabase = this.getReadableDatabase();
// Get everything in `DOMAINS_TABLE` ordered by `DOMAIN_NAME`.
- final String GET_CURSOR_SORTED_BY_DOMAIN = "Select * FROM " + DOMAINS_TABLE +
+ final String GET_CURSOR_ORDERED_BY_DOMAIN = "SELECT " + _ID + ", " + DOMAIN_NAME +
+ " FROM " + DOMAINS_TABLE +
" ORDER BY " + DOMAIN_NAME + " ASC";
// Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. We can't close the `Cursor` because we need to use it in the parent activity.
- return domainsDatabase.rawQuery(GET_CURSOR_SORTED_BY_DOMAIN, null);
+ return domainsDatabase.rawQuery(GET_CURSOR_ORDERED_BY_DOMAIN, null);
+ }
+
+ public Cursor getDomainNameCursorOrderedByDomainExcept(int databaseId) {
+ // Get a readable database handle.
+ SQLiteDatabase domainsDatabase = this.getReadableDatabase();
+
+ // Prepare the SQL statement to select all rows except that with `databaseId`.
+ final String GET_CURSOR_ORDERED_BY_DOMAIN_EXCEPT = "SELECT " + _ID + ", " + DOMAIN_NAME +
+ " FROM " + DOMAINS_TABLE +
+ " WHERE " + _ID + " IS NOT " + databaseId +
+ " ORDER BY " + DOMAIN_NAME + " ASC";
+
+ // Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. We can't close the `Cursor` because we need to use it in the parent activity.
+ return domainsDatabase.rawQuery(GET_CURSOR_ORDERED_BY_DOMAIN_EXCEPT, null);
}
public Cursor getCursorForId(int databaseId) {
// Close the database handle.
domainsDatabase.close();
}
-}
+}
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright 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/>. -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="false"
- android:color="@color/gray_500" />
- <item
- android:state_enabled="true"
- android:color="@color/red_a700" />
-</selector>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright 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/>. -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_enabled="false"
- android:color="@color/gray_500" />
- <item
- android:state_enabled="true"
- android:color="@color/yellow_900" />
-</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/>. -->
-
-<!-- This selector changes the background of activated items in the bookmarks `ListView`. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:state_activated="true"
- android:drawable="@color/blue_100" />
-</selector>
\ No newline at end of file
--- /dev/null
+<!-- `cookies_warning.xml` was created by Google and downloaded from <https://materialdesignicons.com/icon/cookie>. It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true" not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="26dp"
+ android:width="26dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- We have to use a hard coded color code until API >= 21. Then we can use `@color`. -->
+ <path
+ android:fillColor="#44000000"
+ android:pathData="M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z" />
+</vector>
\ No newline at end of file
--- /dev/null
+<!-- `cookies_warning.xml` was created by Google and downloaded from <https://materialdesignicons.com/icon/cookie>. It is released under the Apache License 2.0. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true" not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="26dp"
+ android:width="26dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- We have to use a hard coded color code until API >= 21. Then we can use `@color`. -->
+ <path
+ android:fillColor="#FFD50000"
+ android:pathData="M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z" />
+</vector>
\ 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/>. -->
+
+<!-- This selector changes the background of activated items in the bookmarks `ListView`. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_activated="true"
+ android:drawable="@color/blue_100" />
+</selector>
\ No newline at end of file
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
- android:background="@drawable/bookmarks_list_selector" >
+ android:background="@drawable/listview_item_background_selector" >
<ImageView
android:id="@+id/bookmark_favorite_icon"
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:orientation="horizontal" >
-
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginBottom="26dp"
- android:layout_marginEnd="10dp"
- android:layout_gravity="bottom"
- android:src="@drawable/domains"
- android:tint="@color/blue_800"
- tools:ignore="contentDescription" />
+ android:orientation="vertical">
- <!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
- <android.support.design.widget.TextInputLayout
+ <LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_marginStart="6dp"
- android:layout_marginBottom="14dp" >
+ android:orientation="horizontal" >
- <!-- `android:inputType="textUri"` disables spell check in the `EditText`. -->
- <android.support.design.widget.TextInputEditText
- android:id="@+id/domain_settings_name_edittext"
- android:layout_width="match_parent"
+ <ImageView
android:layout_height="wrap_content"
- android:hint="@string/domain_name"
- android:inputType="textUri" />
- </android.support.design.widget.TextInputLayout>
+ android:layout_width="wrap_content"
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="12dp"
+ android:layout_gravity="bottom"
+ android:src="@drawable/domains"
+ android:tint="@color/blue_800"
+ tools:ignore="contentDescription" />
+
+ <!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+ <android.support.design.widget.TextInputLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginStart="6dp" >
+
+ <!-- `android:inputType="textUri"` disables spell check in the `EditText`. -->
+ <android.support.design.widget.TextInputEditText
+ android:id="@+id/domain_settings_name_edittext"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/domain_name"
+ android:inputType="textUri" />
+ </android.support.design.widget.TextInputLayout>
+ </LinearLayout>
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/domain_name_instructions"
+ android:textSize="12sp"
+ android:layout_marginStart="43dp"
+ android:layout_marginBottom="14dp" />
</LinearLayout>
<!-- JavaScript. -->
android:layout_marginTop="1dp"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/cookies_enabled"
- android:tint="@color/domain_settings_icon_yellow_tint_selector"
+ android:src="@drawable/cookies_disabled"
tools:ignore="contentDescription" />
<Switch
<!-- Third-Party Cookies. -->
<LinearLayout
+ android:id="@+id/domain_settings_third_party_cookies_linearlayout"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal" >
android:layout_marginTop="1dp"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/cookies_enabled"
- android:tint="@color/domain_settings_icon_red_tint_selector"
+ android:src="@drawable/cookies_disabled"
tools:ignore="contentDescription" />
<Switch
android:layout_marginTop="1dp"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/dom_storage_enabled"
- android:tint="@color/domain_settings_icon_yellow_tint_selector"
+ android:src="@drawable/dom_storage_disabled"
tools:ignore="contentDescription" />
<Switch
android:layout_marginTop="1dp"
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/form_data_enabled"
- android:tint="@color/domain_settings_icon_yellow_tint_selector"
+ android:src="@drawable/form_data_disabled"
tools:ignore="contentDescription" />
<Switch
<!-- `android:fitsSystemWindows="true"` moves the AppBar below the status bar. When it is specified the theme should include `<item name="android:windowTranslucentStatus">true</item>` to make the status bar a transparent, darkened overlay. -->
<android.support.design.widget.CoordinatorLayout
+ android:id="@+id/domain_settings_coordinatorlayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
- android:background="@drawable/bookmarks_list_selector" >
+ android:background="@drawable/listview_item_background_selector" >
<ImageView
android:id="@+id/move_to_folder_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
- android:background="@drawable/bookmarks_list_selector" >
+ android:background="@drawable/listview_item_background_selector" >
<ImageView
android:id="@+id/history_favorite_icon_imageview"
<string name="add_domain">Add Domain</string>
<string name="add">Add</string>
<string name="domain_name">Domain name</string>
+ <string name="domain_settings_saved">Domain settings saved</string>
+ <string name="domain_deleted">Domain deleted</string>
+ <string name="domain_name_instructions">*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)</string>
<!-- Guide. -->
<string name="privacy_browser_guide">Privacy Browser Guide</string>