Add domains `Snackbars` and ghosting of switches.
authorSoren Stoutner <soren@stoutner.com>
Wed, 15 Mar 2017 09:18:08 +0000 (02:18 -0700)
committerSoren Stoutner <soren@stoutner.com>
Wed, 15 Mar 2017 09:18:08 +0000 (02:18 -0700)
19 files changed:
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/DomainSettingsActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java
app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java
app/src/main/res/color/domain_settings_icon_red_tint_selector.xml [deleted file]
app/src/main/res/color/domain_settings_icon_yellow_tint_selector.xml [deleted file]
app/src/main/res/drawable/bookmarks_list_selector.xml [deleted file]
app/src/main/res/drawable/cookies_ghosted.xml [new file with mode: 0644]
app/src/main/res/drawable/cookies_warning.xml [new file with mode: 0644]
app/src/main/res/drawable/listview_item_background_selector.xml [new file with mode: 0644]
app/src/main/res/layout/bookmarks_item_linearlayout.xml
app/src/main/res/layout/domain_settings.xml
app/src/main/res/layout/domain_settings_coordinatorlayout.xml
app/src/main/res/layout/move_to_folder_item_linearlayout.xml
app/src/main/res/layout/url_history_item_linearlayout.xml
app/src/main/res/values/strings.xml

index dcb603ff715678d25e6c5511ebe4ad3397804baa..35ac9e34e897dfa425e436ca73fdd551abbb3628 100644 (file)
@@ -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) {
index 571973db0980a626adae9f1dc88977627da83def..43bc7c704e4b9a11d914471fe9d8829c74c864fd 100644 (file)
 
 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;
index 158fd16a6384a794f8d5e0b325c83a7061b81116..e748a7966843415c7d3301cf092d787138efd198 100644 (file)
@@ -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`.
index d0d8c9c0d314628673ea9d893487f8cbc07f5b30..d6462c61ffa1f697e5f54ed043f0c9c631af1a38 100644 (file)
@@ -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.
index aecf8e813d178466d25719821347677e3a9eb991..25467730183c421c62151920c30900b380d1016e 100644 (file)
@@ -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));
+                }
             }
         });
 
index 0daf7f6a9befc6edd66c308dfdda89056011f47f..a743010c485422ed2c8659d6ac919cfd48c512fd 100644 (file)
@@ -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`.
index be88ee6c471bbb2d616088276ab0466d1455ea85..130b0f6cd5541d75f8a3a54f5c2d4a844eeb079b 100644 (file)
@@ -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 (file)
index adead7d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
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 (file)
index 85c7254..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
diff --git a/app/src/main/res/drawable/bookmarks_list_selector.xml b/app/src/main/res/drawable/bookmarks_list_selector.xml
deleted file mode 100644 (file)
index 3f74269..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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
diff --git a/app/src/main/res/drawable/cookies_ghosted.xml b/app/src/main/res/drawable/cookies_ghosted.xml
new file mode 100644 (file)
index 0000000..1c7184f
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `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
diff --git a/app/src/main/res/drawable/cookies_warning.xml b/app/src/main/res/drawable/cookies_warning.xml
new file mode 100644 (file)
index 0000000..51239ad
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- `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
diff --git a/app/src/main/res/drawable/listview_item_background_selector.xml b/app/src/main/res/drawable/listview_item_background_selector.xml
new file mode 100644 (file)
index 0000000..3f74269
--- /dev/null
@@ -0,0 +1,26 @@
+<?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
index 6b09f2f1916e2dc108a85f2d20806df473855e92..73e797ac27efa7f4a2fed246f0a65e7b0da26ff0 100644 (file)
@@ -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" >
 
     <ImageView
         android:id="@+id/bookmark_favorite_icon"
index 1ea1417c46723b2a21824e0649911018787a8ecb..7485ef0063878a10972fd525d0f8b3bccc241ae7 100644 (file)
         <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
index ceb1beacb7e2770b3953e1718d651b5e4fe0613e..da60754fb304b0eb6d8b866bfccc0826c78e7955 100644 (file)
@@ -20,6 +20,7 @@
 
 <!-- `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"
index 8b55b6565673e00a7670f0b145c0f8ba8d968ecf..ed93447d82431fd86bc43f3975e5419bd8a47d55 100644 (file)
@@ -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" >
 
     <ImageView
         android:id="@+id/move_to_folder_icon"
index bcd02ae134e1ac3d80e067c062debaf7bbd2bac2..3c284ac9c6e00b0f026ba336f19348e9615d5d3a 100644 (file)
@@ -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" >
 
     <ImageView
         android:id="@+id/history_favorite_icon_imageview"
index cca1c042501d85e43bc14cfb896853411cb8f28f..7e0c5d5a1eb981067aeba337c31f6c5808271e2f 100644 (file)
     <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>