From 557c44e588c9c881a5a1573667a34d0355cde834 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Wed, 15 Mar 2017 02:18:08 -0700 Subject: [PATCH] Add domains `Snackbars` and ghosting of switches. --- .../activities/BookmarksActivity.java | 19 +-- .../activities/DomainSettingsActivity.java | 58 ++++++- .../activities/DomainsActivity.java | 141 +++++++++++++++-- .../activities/MainWebViewActivity.java | 2 +- .../fragments/DomainSettingsFragment.java | 147 ++++++++++++++---- .../helpers/BookmarksDatabaseHelper.java | 22 +-- .../helpers/DomainsDatabaseHelper.java | 23 ++- ...domain_settings_icon_red_tint_selector.xml | 28 ---- ...ain_settings_icon_yellow_tint_selector.xml | 28 ---- app/src/main/res/drawable/cookies_ghosted.xml | 18 +++ app/src/main/res/drawable/cookies_warning.xml | 18 +++ ... => listview_item_background_selector.xml} | 0 .../layout/bookmarks_item_linearlayout.xml | 2 +- app/src/main/res/layout/domain_settings.xml | 70 +++++---- .../domain_settings_coordinatorlayout.xml | 1 + .../move_to_folder_item_linearlayout.xml | 2 +- .../layout/url_history_item_linearlayout.xml | 2 +- app/src/main/res/values/strings.xml | 3 + 18 files changed, 415 insertions(+), 169 deletions(-) delete mode 100644 app/src/main/res/color/domain_settings_icon_red_tint_selector.xml delete mode 100644 app/src/main/res/color/domain_settings_icon_yellow_tint_selector.xml create mode 100644 app/src/main/res/drawable/cookies_ghosted.xml create mode 100644 app/src/main/res/drawable/cookies_warning.xml rename app/src/main/res/drawable/{bookmarks_list_selector.xml => listview_item_background_selector.xml} (100%) diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java index dcb603ff..35ac9e34 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -405,6 +405,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma // 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. @@ -425,31 +426,17 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma .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) { diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainSettingsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainSettingsActivity.java index 571973db..43bc7c70 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainSettingsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainSettingsActivity.java @@ -19,14 +19,19 @@ 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; @@ -87,7 +92,7 @@ public class DomainSettingsActivity extends AppCompatActivity { // 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`. @@ -135,11 +140,52 @@ public class DomainSettingsActivity extends AppCompatActivity { 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; diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index 158fd16a..e748a796 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -24,6 +24,7 @@ import android.content.Intent; 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; @@ -48,7 +49,7 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; 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; @@ -56,7 +57,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // `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()`. @@ -117,6 +118,11 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // 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); @@ -204,19 +210,119 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // 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; } @@ -234,8 +340,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo } 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) { @@ -259,11 +376,11 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // 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`. diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index d0d8c9c0..d6462c61 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -831,7 +831,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation 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. diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java index aecf8e81..25467730 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java @@ -22,6 +22,7 @@ package com.stoutner.privacybrowser.fragments; 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; @@ -34,6 +35,7 @@ import android.widget.ArrayAdapter; 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; @@ -72,9 +74,10 @@ public class DomainSettingsFragment extends Fragment { 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); @@ -127,40 +130,74 @@ public class DomainSettingsFragment extends Fragment { 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. @@ -213,11 +250,28 @@ public class DomainSettingsFragment extends Fragment { 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)); } } }); @@ -226,8 +280,29 @@ public class DomainSettingsFragment extends Fragment { 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)); + } } }); @@ -236,7 +311,11 @@ public class DomainSettingsFragment extends Fragment { @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)); + } } }); @@ -245,7 +324,11 @@ public class DomainSettingsFragment extends Fragment { @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)); + } } }); @@ -254,7 +337,11 @@ public class DomainSettingsFragment extends Fragment { @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)); + } } }); diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java index 0daf7f6a..a743010c 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java @@ -112,7 +112,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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. @@ -124,7 +124,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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`. @@ -150,7 +150,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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; @@ -164,7 +164,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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"; @@ -182,7 +182,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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; @@ -199,7 +199,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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; @@ -221,7 +221,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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. @@ -236,7 +236,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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"; @@ -264,7 +264,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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"; @@ -279,7 +279,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { 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`. @@ -409,7 +409,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // 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`. diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java index be88ee6c..130b0f6c 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java @@ -68,16 +68,31 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { // 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) { @@ -150,4 +165,4 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { // Close the database handle. domainsDatabase.close(); } -} +} \ No newline at end of file diff --git a/app/src/main/res/color/domain_settings_icon_red_tint_selector.xml b/app/src/main/res/color/domain_settings_icon_red_tint_selector.xml deleted file mode 100644 index adead7d6..00000000 --- a/app/src/main/res/color/domain_settings_icon_red_tint_selector.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/color/domain_settings_icon_yellow_tint_selector.xml b/app/src/main/res/color/domain_settings_icon_yellow_tint_selector.xml deleted file mode 100644 index 85c72543..00000000 --- a/app/src/main/res/color/domain_settings_icon_yellow_tint_selector.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/cookies_ghosted.xml b/app/src/main/res/drawable/cookies_ghosted.xml new file mode 100644 index 00000000..1c7184f3 --- /dev/null +++ b/app/src/main/res/drawable/cookies_ghosted.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/cookies_warning.xml b/app/src/main/res/drawable/cookies_warning.xml new file mode 100644 index 00000000..51239adc --- /dev/null +++ b/app/src/main/res/drawable/cookies_warning.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bookmarks_list_selector.xml b/app/src/main/res/drawable/listview_item_background_selector.xml similarity index 100% rename from app/src/main/res/drawable/bookmarks_list_selector.xml rename to app/src/main/res/drawable/listview_item_background_selector.xml diff --git a/app/src/main/res/layout/bookmarks_item_linearlayout.xml b/app/src/main/res/layout/bookmarks_item_linearlayout.xml index 6b09f2f1..73e797ac 100644 --- a/app/src/main/res/layout/bookmarks_item_linearlayout.xml +++ b/app/src/main/res/layout/bookmarks_item_linearlayout.xml @@ -25,7 +25,7 @@ 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" > - - + android:orientation="vertical"> - - + android:orientation="horizontal" > - - - + 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" /> + + + + + + + + + + @@ -108,8 +121,7 @@ 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" /> @@ -136,8 +149,7 @@ 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" /> true` to make the status bar a transparent, darkened overlay. --> + android:background="@drawable/listview_item_background_selector" > + android:background="@drawable/listview_item_background_selector" > Add Domain Add Domain name + Domain settings saved + Domain deleted + *. may be prepended to a domain to include all subdomains (eg. *.stoutner.com) Privacy Browser Guide -- 2.45.2