Wait to enable the delete icon until the previous domain has been deleted. Fixes...
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / activities / DomainsActivity.java
1 /*
2  * Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.activities;
21
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.support.design.widget.FloatingActionButton;
27 import android.support.design.widget.Snackbar;
28 import android.support.v4.app.FragmentManager;
29 import android.support.v4.app.NavUtils;
30 import android.support.v7.app.ActionBar;
31 import android.support.v7.app.AppCompatActivity;
32 import android.support.v7.app.AppCompatDialogFragment;
33 import android.support.v7.widget.Toolbar;
34 import android.view.Menu;
35 import android.view.MenuItem;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.CursorAdapter;
39 import android.widget.EditText;
40 import android.widget.ListView;
41 import android.widget.Spinner;
42 import android.widget.Switch;
43 import android.widget.TextView;
44
45 import com.stoutner.privacybrowser.R;
46 import com.stoutner.privacybrowser.dialogs.AddDomainDialog;
47 import com.stoutner.privacybrowser.fragments.DomainSettingsFragment;
48 import com.stoutner.privacybrowser.fragments.DomainsListFragment;
49 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
50
51 public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener {
52     // `twoPanedMode` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreate()` and `populateDomainsListView()`.
53     public static boolean twoPanedMode;
54
55     // `databaseId` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `saveDomainSettings()` and `populateDomainsListView()`.
56     public static int currentDomainDatabaseId;
57
58     // `domainSettingsFragmentDisplayed` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`.
59     public static boolean domainSettingsFragmentDisplayed;
60
61     // `deleteMenuItem` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`.
62     public static MenuItem deleteMenuItem;
63
64     // `undoDeleteSnackbar` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onOptionsItemSelected()`.
65     public static Snackbar undoDeleteSnackbar;
66
67     // `dismissingSnackbar` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onOptionsItemSelected()`.
68     public static boolean dismissingSnackbar;
69
70     // `context` is used in `onCreate()`, `onOptionsItemSelected()`, and `onAddDomain()`.
71     private Context context;
72
73     // `supportFragmentManager` is used in `onCreate()`.
74     private FragmentManager supportFragmentManager;
75
76     // `domainsDatabaseHelper` is used in `onCreate()` and `saveDomainSettings()`.
77     private static DomainsDatabaseHelper domainsDatabaseHelper;
78
79     // `domainsListView` is used in `onCreate()` and `populateDomainsList()`.
80     private ListView domainsListView;
81
82     // `addDomainFAB` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`.
83     private FloatingActionButton addDomainFAB;
84
85     @Override
86     protected void onCreate(Bundle savedInstanceState) {
87         // Set the activity theme.
88         if (MainWebViewActivity.darkTheme) {
89             setTheme(R.style.PrivacyBrowserDark_SecondaryActivity);
90         } else {
91             setTheme(R.style.PrivacyBrowserLight_SecondaryActivity);
92         }
93
94         // Run the default commands.
95         super.onCreate(savedInstanceState);
96
97         // Set the content view.
98         setContentView(R.layout.domains_coordinatorlayout);
99
100         // Get a handle for the context.
101         context = this;
102
103         // Get a handle for the fragment manager.
104         supportFragmentManager = getSupportFragmentManager();
105
106         // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21.
107         final Toolbar domainsAppBar = (Toolbar) findViewById(R.id.domains_toolbar);
108         setSupportActionBar(domainsAppBar);
109
110         // Display the home arrow on `SupportActionBar`.
111         ActionBar appBar = getSupportActionBar();
112         assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that `appBar` might be null.
113         appBar.setDisplayHomeAsUpEnabled(true);
114
115         // Initialize the database handler.  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`.
116         domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
117
118         // Determine if we are in two pane mode.  `domain_settings_fragment_container` does not exist on devices with a width less than 900dp.
119         twoPanedMode = (findViewById(R.id.domain_settings_fragment_container) != null);
120
121         // Display `DomainsListFragment`.
122         DomainsListFragment domainsListFragment = new DomainsListFragment();
123         supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
124         supportFragmentManager.executePendingTransactions();
125
126         // Configure `addDomainFAB`.
127         addDomainFAB = (FloatingActionButton) findViewById(R.id.add_domain_fab);
128         addDomainFAB.setOnClickListener(new View.OnClickListener() {
129             @Override
130             public void onClick(View view) {
131                 // Show the `AddDomainDialog` `AlertDialog` and name the instance `@string/add_domain`.
132                 AppCompatDialogFragment addDomainDialog = new AddDomainDialog();
133                 addDomainDialog.show(supportFragmentManager, getResources().getString(R.string.add_domain));
134             }
135         });
136     }
137
138     @Override
139     public boolean onCreateOptionsMenu(Menu menu) {
140         // Inflate the menu.
141         getMenuInflater().inflate(R.menu.domains_options_menu, menu);
142
143         // Store `deleteMenuItem` for future use.
144         deleteMenuItem = menu.findItem(R.id.delete_domain);
145
146         // Only display `deleteMenuItem` (initially) in two-paned mode.
147         deleteMenuItem.setVisible(twoPanedMode);
148
149         // Populate the list of domains.  We have to do this from `onCreateOptionsMenu()` instead of `onCreate()` because `populateDomainsListView()` needs the `deleteMenuItem` to be inflated.  `-1` highlights the first domain.
150         populateDomainsListView(-1);
151
152         // Success!
153         return true;
154     }
155
156     @Override
157     public boolean onOptionsItemSelected(MenuItem menuItem) {
158         // Get the ID of the `MenuItem` that was selected.
159         int menuItemID = menuItem.getItemId();
160
161         switch (menuItemID) {
162             case android.R.id.home:  // The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
163                 if (twoPanedMode) {  // The device is in two-paned mode.
164                     // Save the current domain settings if the domain settings fragment is displayed.
165                     if (findViewById(R.id.domain_settings_scrollview) != null) {
166                         saveDomainSettings();
167                     }
168
169                     // Go home.
170                     NavUtils.navigateUpFromSameTask(this);
171                 } else if (domainSettingsFragmentDisplayed) {  // The device is in single-paned mode and `DomainSettingsFragment` is displayed.
172                     // Save the current domain settings.
173                     saveDomainSettings();
174
175                     // Display `DomainsListFragment`.
176                     DomainsListFragment domainsListFragment = new DomainsListFragment();
177                     supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
178                     supportFragmentManager.executePendingTransactions();
179
180                     // Populate the list of domains.  `-1` highlights the first domain if in two-paned mode.  It has no effect in single-paned mode.
181                     populateDomainsListView(-1);
182
183                     // Update `domainSettingsFragmentDisplayed`.
184                     domainSettingsFragmentDisplayed = false;
185
186                     // Display `addDomainFAB`.
187                     addDomainFAB.setVisibility(View.VISIBLE);
188
189                     // Hide `deleteMenuItem`.
190                     deleteMenuItem.setVisible(false);
191                 } else {  // The device is in single-paned mode and `DomainsListFragment` is displayed.
192                     // Go home.
193                     NavUtils.navigateUpFromSameTask(this);
194                 }
195                 break;
196
197             case R.id.delete_domain:
198                 // Store a copy of `currentDomainDatabaseId` because it could change while the `Snackbar` is displayed.
199                 final int databaseIdToDelete = currentDomainDatabaseId;
200
201                 // Store the deleted domain position, which is needed if `Undo` is selected in the `Snackbar`.
202                 final int deletedDomainPosition = domainsListView.getCheckedItemPosition();
203
204                 // Update the fragments and menu items.
205                 if (twoPanedMode) {  // Two-paned mode.
206                     // Disable the options `MenuItems`.
207                     deleteMenuItem.setEnabled(false);
208                     deleteMenuItem.setIcon(R.drawable.delete_blue);
209
210                     // Remove the domain settings fragment.
211                     supportFragmentManager.beginTransaction().remove(supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)).commit();
212                 } else {  // Single-paned mode.
213                     // Display `DomainsListFragment`.
214                     DomainsListFragment domainsListFragment = new DomainsListFragment();
215                     supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
216                     supportFragmentManager.executePendingTransactions();
217
218                     // Update `domainSettingsFragmentDisplayed`.
219                     domainSettingsFragmentDisplayed = false;
220
221                     // Display `addDomainFAB`.
222                     addDomainFAB.setVisibility(View.VISIBLE);
223
224                     // Hide `deleteMenuItem`.
225                     deleteMenuItem.setVisible(false);
226                 }
227
228                 // Get a `Cursor` that does not show the domain to be deleted.
229                 Cursor domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete);
230
231                 // Setup `domainsPendingDeleteCursorAdapter` with `this` context.  `false` disables `autoRequery`.
232                 CursorAdapter domainsPendingDeleteCursorAdapter = new CursorAdapter(this, domainsPendingDeleteCursor, false) {
233                     @Override
234                     public View newView(Context context, Cursor cursor, ViewGroup parent) {
235                         // Inflate the individual item layout.  `false` does not attach it to the root.
236                         return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
237                     }
238
239                     @Override
240                     public void bindView(View view, Context context, Cursor cursor) {
241                         // Set the domain name.
242                         String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
243                         TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview);
244                         domainNameTextView.setText(domainNameString);
245                     }
246                 };
247
248                 // Update the handle for the current `domains_listview`.
249                 domainsListView = (ListView) findViewById(R.id.domains_listview);
250
251                 // Update the `ListView`.
252                 domainsListView.setAdapter(domainsPendingDeleteCursorAdapter);
253
254                 // Display a `Snackbar`.
255                 undoDeleteSnackbar = Snackbar.make(domainsListView, R.string.domain_deleted, Snackbar.LENGTH_LONG)
256                         .setAction(R.string.undo, new View.OnClickListener() {
257                             @Override
258                             public void onClick(View v) {
259                                 // Do nothing because everything will be handled by `onDismissed()` below.
260                             }
261                         })
262                         .addCallback(new Snackbar.Callback() {
263                             @Override
264                             public void onDismissed(Snackbar snackbar, int event) {
265                                 switch (event) {
266                                     // The user pushed the `Undo` button.
267                                     case Snackbar.Callback.DISMISS_EVENT_ACTION:
268                                         // Store `databaseId` in `argumentsBundle`.
269                                         Bundle argumentsBundle = new Bundle();
270                                         argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete);
271
272                                         // Add `argumentsBundle` to `domainSettingsFragment`.
273                                         DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
274                                         domainSettingsFragment.setArguments(argumentsBundle);
275
276                                         // Display the correct fragments.
277                                         if (twoPanedMode) {  // The device in in two-paned mode.
278                                             // Get a `Cursor` with the current contents of the domains database.
279                                             Cursor undoDeleteDomainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
280
281                                             // Setup `domainsCursorAdapter` with `this` context.  `false` disables `autoRequery`.
282                                             CursorAdapter undoDeleteDomainsCursorAdapter = new CursorAdapter(context, undoDeleteDomainsCursor, false) {
283                                                 @Override
284                                                 public View newView(Context context, Cursor cursor, ViewGroup parent) {
285                                                     // Inflate the individual item layout.  `false` does not attach it to the root.
286                                                     return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
287                                                 }
288
289                                                 @Override
290                                                 public void bindView(View view, Context context, Cursor cursor) {
291                                                     // Set the domain name.
292                                                     String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
293                                                     TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview);
294                                                     domainNameTextView.setText(domainNameString);
295                                                 }
296                                             };
297
298                                             // Update the `ListView`.
299                                             domainsListView.setAdapter(undoDeleteDomainsCursorAdapter);
300                                             // Select the previously deleted domain in `domainsListView`.
301                                             domainsListView.setItemChecked(deletedDomainPosition, true);
302
303                                             // Display `domainSettingsFragment`.
304                                             supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit();
305
306                                             // Enable the options `MenuItems`.
307                                             deleteMenuItem.setEnabled(true);
308                                             deleteMenuItem.setIcon(R.drawable.delete_light);
309                                         } else {  // The device in in one-paned mode.
310                                             // Display `domainSettingsFragment`.
311                                             supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
312
313                                             // Hide `add_domain_fab`.
314                                             FloatingActionButton addDomainFAB = (FloatingActionButton) findViewById(R.id.add_domain_fab);
315                                             addDomainFAB.setVisibility(View.GONE);
316
317                                             // Show and enable `deleteMenuItem`.
318                                             deleteMenuItem.setVisible(true);
319
320                                             // Set `domainSettingsFragmentDisplayed`.
321                                             domainSettingsFragmentDisplayed = true;
322
323                                             // Display `domainSettingsFragment`.
324                                             supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
325                                         }
326                                         break;
327
328                                     // The `Snackbar` was dismissed without the `Undo` button being pushed.
329                                     default:
330                                         // Delete the selected domain.
331                                         domainsDatabaseHelper.deleteDomain(databaseIdToDelete);
332
333                                         // enable `deleteMenuItem` if the system was waiting for a `Snackbar` to be dismissed.
334                                         if (DomainsActivity.dismissingSnackbar) {
335                                             // Create a `Runnable` to enable the delete menu item.
336                                             Runnable enableDeleteMenuItemRunnable = new Runnable() {
337                                                 @Override
338                                                 public void run() {
339                                                     // Enable `deleteMenuItem` according to the display mode.
340                                                     if (twoPanedMode) {  // Two-paned mode.
341                                                         // Enable `deleteMenuItem`.
342                                                         deleteMenuItem.setEnabled(true);
343
344                                                         // Set the delete icon according to the theme.
345                                                         if (MainWebViewActivity.darkTheme) {
346                                                             deleteMenuItem.setIcon(R.drawable.delete_dark);
347                                                         } else {
348                                                             deleteMenuItem.setIcon(R.drawable.delete_light);
349                                                         }
350                                                     } else {  // Single-paned mode.
351                                                         // Show `deleteMenuItem`.
352                                                         deleteMenuItem.setVisible(true);
353                                                     }
354
355                                                     // Reset `dismissingSnackbar`.
356                                                     dismissingSnackbar = false;
357                                                 }
358                                             };
359
360                                             // Run `enableDeleteMenuItemRunnable` after 100 milliseconds to make sure that the previous domain has been deleted from the database.
361                                             Handler handler = new Handler();
362                                             handler.postDelayed(enableDeleteMenuItemRunnable, 100);
363                                         }
364                                         break;
365                                 }
366                             }
367                         });
368                 undoDeleteSnackbar.show();
369                 break;
370         }
371
372         // Consume the event.
373         return true;
374     }
375
376     // Control what the navigation bar back button does.
377     @Override
378     public void onBackPressed() {
379         if (twoPanedMode) {  // The device is in two-paned mode.
380             // Save the current domain settings if the domain settings fragment is displayed.
381             if (findViewById(R.id.domain_settings_scrollview) != null) {
382                 saveDomainSettings();
383             }
384
385             // Go home.
386             NavUtils.navigateUpFromSameTask(this);
387         } else if (domainSettingsFragmentDisplayed) {  // The device is in single-paned mode and `DomainSettingsFragment` is displayed.
388             // Save the current domain settings.
389             saveDomainSettings();
390
391             // Display `DomainsListFragment`.
392             DomainsListFragment domainsListFragment = new DomainsListFragment();
393             supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
394             supportFragmentManager.executePendingTransactions();
395
396             // Populate the list of domains.  `-1` highlights the first domain if in two-paned mode.  It has no effect in single-paned mode.
397             populateDomainsListView(-1);
398
399             // Update `domainSettingsFragmentDisplayed`.
400             domainSettingsFragmentDisplayed = false;
401
402             // Display `addDomainFAB`.
403             addDomainFAB.setVisibility(View.VISIBLE);
404
405             // Hide `deleteMenuItem`.
406             deleteMenuItem.setVisible(false);
407         } else {  // The device is in single-paned mode and `DomainsListFragment` is displayed.
408             // Pass `onBackPressed()` to the system.
409             super.onBackPressed();
410         }
411     }
412
413     @Override
414     public void onAddDomain(AppCompatDialogFragment dialogFragment) {
415         // Dismiss `undoDeleteSnackbar` if it is currently displayed.
416         if ((undoDeleteSnackbar != null) && (undoDeleteSnackbar.isShown())) {
417             undoDeleteSnackbar.dismiss();
418         }
419
420         // Get the `domainNameEditText` from `dialogFragment` and extract the string.
421         EditText domainNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.domain_name_edittext);
422         String domainNameString = domainNameEditText.getText().toString();
423
424         // Create the domain and store the database ID in `currentDomainDatabaseId`.
425         currentDomainDatabaseId = domainsDatabaseHelper.addDomain(domainNameString);
426
427         // Display the newly created domain.
428         if (twoPanedMode) {  // The device in in two-paned mode.
429             populateDomainsListView(currentDomainDatabaseId);
430         } else {  // The device is in single-paned mode.
431             // Hide `add_domain_fab`.
432             addDomainFAB.setVisibility(View.GONE);
433
434             // Show and enable `deleteMenuItem`.
435             DomainsActivity.deleteMenuItem.setVisible(true);
436
437             // Set `domainSettingsFragmentDisplayed`.
438             DomainsActivity.domainSettingsFragmentDisplayed = true;
439
440             // Add `currentDomainDatabaseId` to `argumentsBundle`.
441             Bundle argumentsBundle = new Bundle();
442             argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
443
444             // Add `argumentsBundle` to `domainSettingsFragment`.
445             DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
446             domainSettingsFragment.setArguments(argumentsBundle);
447
448             // Display `domainSettingsFragment`.
449             supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
450         }
451     }
452
453     private void saveDomainSettings() {
454         // Get handles for the domain settings.
455         EditText domainNameEditText = (EditText) findViewById(R.id.domain_settings_name_edittext);
456         Switch javaScriptEnabledSwitch = (Switch) findViewById(R.id.domain_settings_javascript_switch);
457         Switch firstPartyCookiesEnabledSwitch = (Switch) findViewById(R.id.domain_settings_first_party_cookies_switch);
458         Switch thirdPartyCookiesEnabledSwitch = (Switch) findViewById(R.id.domain_settings_third_party_cookies_switch);
459         Switch domStorageEnabledSwitch = (Switch) findViewById(R.id.domain_settings_dom_storage_switch);
460         Switch formDataEnabledSwitch = (Switch) findViewById(R.id.domain_settings_form_data_switch);
461         Spinner userAgentSpinner = (Spinner) findViewById(R.id.domain_settings_user_agent_spinner);
462         EditText customUserAgentEditText = (EditText) findViewById(R.id.domain_settings_custom_user_agent_edittext);
463         Spinner fontSizeSpinner = (Spinner) findViewById(R.id.domain_settings_font_size_spinner);
464         Spinner displayWebpageImagesSpinner = (Spinner) findViewById(R.id.domain_settings_display_webpage_images_spinner);
465
466         // Extract the data for the domain settings.
467         String domainNameString = domainNameEditText.getText().toString();
468         boolean javaScriptEnabledBoolean = javaScriptEnabledSwitch.isChecked();
469         boolean firstPartyCookiesEnabledBoolean = firstPartyCookiesEnabledSwitch.isChecked();
470         boolean thirdPartyCookiesEnabledBoolean = thirdPartyCookiesEnabledSwitch.isChecked();
471         boolean domStorageEnabledEnabledBoolean  = domStorageEnabledSwitch.isChecked();
472         boolean formDataEnabledBoolean = formDataEnabledSwitch.isChecked();
473         int userAgentPositionInt = userAgentSpinner.getSelectedItemPosition();
474         int fontSizePositionInt = fontSizeSpinner.getSelectedItemPosition();
475         int displayWebpageImagesInt = displayWebpageImagesSpinner.getSelectedItemPosition();
476
477         // Get the data for the `Spinners` from the entry values string arrays.
478         String userAgentString = getResources().getStringArray(R.array.domain_settings_user_agent_entry_values)[userAgentPositionInt];
479         int fontSizeInt = Integer.parseInt(getResources().getStringArray(R.array.domain_settings_font_size_entry_values)[fontSizePositionInt]);
480
481         // Check to see if we are using a custom user agent.
482         if (userAgentString.equals("Custom user agent")) {
483             // Set `userAgentString` to the custom user agent string.
484             userAgentString = customUserAgentEditText.getText().toString();
485         }
486
487         // Save the domain settings.
488         domainsDatabaseHelper.saveDomain(currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, formDataEnabledBoolean, userAgentString, fontSizeInt,
489                 displayWebpageImagesInt);
490     }
491
492     private void populateDomainsListView(final int highlightedDomainDatabaseId) {
493         // get a handle for the current `domains_listview`.
494         domainsListView = (ListView) findViewById(R.id.domains_listview);
495
496         // Get a `Cursor` with the current contents of the domains database.
497         Cursor domainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
498
499         // Setup `domainsCursorAdapter` with `this` context.  `false` disables `autoRequery`.
500         CursorAdapter domainsCursorAdapter = new CursorAdapter(context, domainsCursor, false) {
501             @Override
502             public View newView(Context context, Cursor cursor, ViewGroup parent) {
503                 // Inflate the individual item layout.  `false` does not attach it to the root.
504                 return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
505             }
506
507             @Override
508             public void bindView(View view, Context context, Cursor cursor) {
509                 // Set the domain name.
510                 String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
511                 TextView domainNameTextView = (TextView) view.findViewById(R.id.domain_name_textview);
512                 domainNameTextView.setText(domainNameString);
513             }
514         };
515
516         // Update the `ListView`.
517         domainsListView.setAdapter(domainsCursorAdapter);
518
519         // Display the domain settings in the second pane if operating in two pane mode and the database contains at least one domain.
520         if (DomainsActivity.twoPanedMode && (domainsCursor.getCount() > 0)) {  // Two-paned mode is enabled and there is at least one domain.
521             // Initialize `highlightedDomainPosition`.
522             int highlightedDomainPosition = 0;
523
524             // Get the cursor position for the highlighted domain.
525             for (int i = 0; i < domainsCursor.getCount(); i++) {
526                 // Move to position `i` in the cursor.
527                 domainsCursor.moveToPosition(i);
528
529                 // Get the database ID for this position.
530                 int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
531
532                 // Set `highlightedDomainPosition` if the database ID for this matches `highlightedDomainDatabaseId`.
533                 if (highlightedDomainDatabaseId == currentDatabaseId) {
534                     highlightedDomainPosition = i;
535                 }
536             }
537
538             // Select the highlighted domain.
539             domainsListView.setItemChecked(highlightedDomainPosition, true);
540
541             // Get the `databaseId` for the highlighted domain.
542             domainsCursor.moveToPosition(highlightedDomainPosition);
543             currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
544
545             // Store `databaseId` in `argumentsBundle`.
546             Bundle argumentsBundle = new Bundle();
547             argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
548
549             // Add `argumentsBundle` to `domainSettingsFragment`.
550             DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
551             domainSettingsFragment.setArguments(argumentsBundle);
552
553             // Display `domainSettingsFragment`.
554             supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit();
555
556             // Enable the options `MenuItems`.
557             deleteMenuItem.setEnabled(true);
558
559             // Set the delete icon according to the theme.
560             if (MainWebViewActivity.darkTheme) {
561                 deleteMenuItem.setIcon(R.drawable.delete_dark);
562             } else {
563                 deleteMenuItem.setIcon(R.drawable.delete_light);
564             }
565         } else if (twoPanedMode) {  // Two-paned mode is enabled but there are no domains.
566             // Disable the options `MenuItems`.
567             deleteMenuItem.setEnabled(false);
568             deleteMenuItem.setIcon(R.drawable.delete_blue);
569         }
570     }
571 }