Fix scrolling to new tabs.
authorSoren Stoutner <soren@stoutner.com>
Mon, 22 Apr 2019 20:16:42 +0000 (13:16 -0700)
committerSoren Stoutner <soren@stoutner.com>
Mon, 22 Apr 2019 20:16:42 +0000 (13:16 -0700)
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistoryDialog.java
app/src/main/res/layout/custom_tab_view.xml [deleted file]
app/src/main/res/layout/guide_coordinatorlayout.xml
app/src/main/res/layout/main_framelayout.xml
app/src/main/res/layout/tab_custom_view.xml [new file with mode: 0644]
app/src/main/res/values-v21/styles.xml
app/src/main/res/values/strings.xml
app/src/main/res/values/styles.xml

index d7e4e69132a9886cfa017710fab1d93ab7e92839..b4996aae41d789d070ebca470ad08b2ee7124aeb 100644 (file)
@@ -71,7 +71,6 @@ import android.webkit.CookieManager;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.ValueCallback;
-import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebSettings;
@@ -124,7 +123,6 @@ import com.stoutner.privacybrowser.dialogs.DownloadLocationPermissionDialog;
 import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog;
 import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog;
 import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
-import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
 import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
 import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
 import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
@@ -154,17 +152,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-// TODO.  Store up reloads for tabs that are not visible.
 // TODO.  New tabs are white in dark mode.
 // TODO.  Hide the tabs in full screen mode.
 // TODO.  Find on page.
-// TODO.  Use TabLayout.setScrollPosition to scroll to new tabs.
 
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
-        EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener, PinnedMismatchDialog.PinnedMismatchListener,
-        UrlHistoryDialog.UrlHistoryListener {
+        EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener {
 
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
     public static String orbotStatus;
@@ -488,16 +483,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set the current WebView.
                 setCurrentWebView(position);
 
-                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled via swiping in the view pager.
+                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
                 if (tabLayout.getSelectedTabPosition() != position) {
-                    // Get a handle for the corresponding tab.
-                    TabLayout.Tab correspondingTab = tabLayout.getTabAt(position);
+                    // Create a handler to select the tab.
+                    Handler selectTabHandler = new Handler();
 
-                    // Assert that the corresponding tab is not null.
-                    assert correspondingTab != null;
+                    // Create a runnable select the new tab.
+                    Runnable selectTabRunnable = () -> {
+                        // Get a handle for the tab.
+                        TabLayout.Tab tab = tabLayout.getTabAt(position);
 
-                    // Select the corresponding tab.
-                    correspondingTab.select();
+                        // Assert that the tab is not null.
+                        assert tab != null;
+
+                        // Select the tab.
+                        tab.select();
+                    };
+
+                    // Select the tab layout after 100 milliseconds, which leaves enough time for a new tab to be created.
+                    selectTabHandler.postDelayed(selectTabRunnable, 100);
                 }
             }
 
@@ -2272,11 +2276,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 break;
 
             case R.id.history:
-                // Get the `WebBackForwardList`.
-                WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+                // Instantiate the URL history dialog.
+                DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
 
-                // Show the URL history dialog and name this instance `R.string.history`.
-                DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList);
+                // Show the URL history dialog.
                 urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
                 break;
 
@@ -3104,47 +3107,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
     }
 
-    @Override
-    public void onPinnedMismatchBack() {  // TODO.  Move this logic to the dialog.
-        if (currentWebView.canGoBack()) {  // There is a back page in the history.
-            // Reset the current domain name so that navigation works if third-party requests are blocked.
-            currentWebView.resetCurrentDomainName();
-
-            // Set navigating history so that the domain settings are applied when the new URL is loaded.
-            currentWebView.setNavigatingHistory(true);
-
-            // Go back.
-            currentWebView.goBack();
-        } else {  // There are no pages to go back to.
-            // Load a blank page
-            loadUrl("");
-        }
-    }
-
-    @Override
-    public void onPinnedMismatchProceed() {  // TODO.  Move this logic to the dialog.
-        // Do not check the pinned information for this domain again until the domain changes.
-        currentWebView.setIgnorePinnedDomainInformation(true);
-    }
-
-    @Override
-    public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {  // TODO.  Move this logic to the dialog.
-        // Reset the current domain name so that navigation works if third-party requests are blocked.
-        currentWebView.resetCurrentDomainName();
-
-        // Set navigating history so that the domain settings are applied when the new URL is loaded.
-        currentWebView.setNavigatingHistory(true);
-
-        // Load the history entry.
-        currentWebView.goBackOrForward(moveBackOrForwardSteps);
-    }
-
-    @Override
-    public void onClearHistory() {  // TODO.  Move this logic to the dialog.
-        // Clear the history.
-        currentWebView.clearHistory();
-    }
-
     // Override `onBackPressed` to handle the navigation drawer and and the WebView.
     @Override
     public void onBackPressed() {
@@ -3174,9 +3136,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Go back.
             currentWebView.goBack();
-        } else {  // There isn't anything to do in Privacy Browser.
-            // Pass `onBackPressed()` to the system.
-            super.onBackPressed();
+        } else {  // There is nothing else to do.
+            // Load a blank website.
+            loadUrl("");
         }
     }
 
@@ -3401,7 +3363,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     // `reloadWebsite` is used if returning from the Domains activity.  Otherwise JavaScript might not function correctly if it is newly enabled.
     @SuppressLint("SetJavaScriptEnabled")
-    private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
+    private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
         // Store a copy of the current user agent to track changes for the return boolean.
         String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
 
@@ -3429,30 +3391,37 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             nestedScrollWebView.clearPinnedIpAddresses();
 
             // Reset the favorite icon if specified.
-            if (resetFavoriteIcon) {
+            if (resetTab) {
                 // Initialize the favorite icon.
                 nestedScrollWebView.initializeFavoriteIcon();
 
+                // Get the current page position.
+                int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
                 // Get a handle for the tab layout.
                 TabLayout tabLayout = findViewById(R.id.tablayout);
 
-                // Get the current tab.
-                TabLayout.Tab currentTab = tabLayout.getTabAt(tabLayout.getSelectedTabPosition());  // TODO.  We need to get the tab for this WebView, which might not be the current tab.
+                // Get the corresponding tab.
+                TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
 
-                // Remove the warning below that the current tab might be null.
-                assert currentTab != null;
+                // Remove the warning below that the tab might be null.
+                assert tab != null;
 
-                // Get the current tab custom view.
-                View currentTabCustomView = currentTab.getCustomView();
+                // Get the tab custom view.
+                View tabCustomView = tab.getCustomView();
 
-                // Remove the warning below that the current tab custom view might be null.
-                assert currentTabCustomView != null;
+                // Remove the warning below that the tab custom view might be null.
+                assert tabCustomView != null;
 
-                // Get the current tab favorite icon image view.
-                ImageView currentTabFavoriteIconImageView = currentTabCustomView.findViewById(R.id.favorite_icon_imageview);
+                // Get the tab views.
+                ImageView tabFavoriteIconImageView = tabCustomView.findViewById(R.id.favorite_icon_imageview);
+                TextView tabTitleTextView = tabCustomView.findViewById(R.id.title_textview);
 
                 // Set the default favorite icon as the favorite icon for this tab.
-                currentTabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true));
+                tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true));
+
+                // Set the loading title text.
+                tabTitleTextView.setText(R.string.loading);
             }
 
             // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
@@ -4186,7 +4155,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         assert newTab != null;
 
         // Set a custom view on the new tab.
-        newTab.setCustomView(R.layout.custom_tab_view);
+        newTab.setCustomView(R.layout.tab_custom_view);
 
         // Add the new WebView page.
         webViewPagerAdapter.addPage(newTabNumber, webViewPager);
@@ -4504,7 +4473,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             }
                         };
 
-                        // Displaying of `mainWebView` after 500 milliseconds.
+                        // Display the WebView after 500 milliseconds.
                         displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
                     });
                 }
index fbf1f90fb143385b97842f671b79014229a7fc7c..72fc85aeeee2752e1968bbf638166449f9b81e27 100644 (file)
@@ -60,7 +60,6 @@ import androidx.viewpager.widget.PagerAdapter;
 
 public class PinnedMismatchDialog extends DialogFragment {
     // Declare the class variables.
-    private PinnedMismatchListener pinnedMismatchListener;
     private NestedScrollWebView nestedScrollWebView;
     private String currentSslIssuedToCName;
     private String currentSslIssuedToOName;
@@ -71,22 +70,6 @@ public class PinnedMismatchDialog extends DialogFragment {
     private Date currentSslStartDate;
     private Date currentSslEndDate;
 
-    // The public interface is used to send information back to the parent activity.
-    public interface PinnedMismatchListener {
-        void onPinnedMismatchBack();
-
-        void onPinnedMismatchProceed();
-    }
-
-    // Check to make sure that the parent activity implements the listener.
-    public void onAttach(Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for `PinnedSslCertificateMismatchListener` from the launching context.
-        pinnedMismatchListener = (PinnedMismatchListener) context;
-    }
-
     public static PinnedMismatchDialog displayDialog(long webViewFragmentId) {
         // Create an arguments bundle.
         Bundle argumentsBundle = new Bundle();
@@ -225,16 +208,27 @@ public class PinnedMismatchDialog extends DialogFragment {
             }
         });
 
-        // Setup the negative button.
+        // Setup the back button.
         dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> {
-            // Call the `onSslMismatchBack` public interface to send the `WebView` back one page.
-            pinnedMismatchListener.onPinnedMismatchBack();
+            if (nestedScrollWebView.canGoBack()) {  // There is a back page in the history.
+                // Reset the current domain name so that navigation works if third-party requests are blocked.
+                nestedScrollWebView.resetCurrentDomainName();
+
+                // Set navigating history so that the domain settings are applied when the new URL is loaded.
+                nestedScrollWebView.setNavigatingHistory(true);
+
+                // Go back.
+                nestedScrollWebView.goBack();
+            } else {  // There are no pages to go back to.
+                // Load a blank page
+                nestedScrollWebView.loadUrl("");
+            }
         });
 
-        // Setup the positive button.
+        // Setup the proceed button.
         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
-            // Call the `onSslMismatchProceed` public interface.
-            pinnedMismatchListener.onPinnedMismatchProceed();
+            // Do not check the pinned information for this domain again until the domain changes.
+            nestedScrollWebView.setIgnorePinnedDomainInformation(true);
         });
 
         // Set the title.
index 13b8c3311e49b4233b540b1a379682b06e0fa3e4..366566f76b9bd3d5392478735f4d3c424caf814f 100644 (file)
@@ -22,16 +22,13 @@ package com.stoutner.privacybrowser.dialogs;
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
-import android.util.Base64;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
@@ -44,56 +41,78 @@ import androidx.core.content.ContextCompat;
 import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
 
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.adapters.HistoryArrayAdapter;
 import com.stoutner.privacybrowser.definitions.History;
+import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
 
-import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 
 public class UrlHistoryDialog extends DialogFragment{
-    // Declare the class variables.
-    private final ArrayList<History> historyArrayList = new ArrayList<>();
-    private int currentPageId;
+    public static UrlHistoryDialog loadBackForwardList(long webViewFragmentId) {
+        // Create an arguments bundle.
+        Bundle argumentsBundle = new Bundle();
 
-    // Create a URL history listener.
-    private UrlHistoryListener urlHistoryListener;
+        // Store the WebView fragment ID in the bundle.
+        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
 
+        // Create a new instance of the URL history dialog.
+        UrlHistoryDialog urlHistoryDialog = new UrlHistoryDialog();
 
-    // The public interface is used to send information back to the parent activity.
-    public interface UrlHistoryListener {
-        // Send back the number of steps to move forward or back.
-        void onUrlHistoryEntrySelected(int moveBackOrForwardSteps);
+        // Add the arguments bundle to this instance.
+        urlHistoryDialog.setArguments(argumentsBundle);
 
-        // Clear the history.
-        void onClearHistory();
+        // Return the new URL history dialog.
+        return urlHistoryDialog;
     }
 
     @Override
-    public void onAttach(Context context) {
-        super.onAttach(context);
-
-        // Check to make sure tha the parent activity implements the listener.
-        try {
-            urlHistoryListener = (UrlHistoryListener) context;
-        } catch (ClassCastException exception) {
-            throw new ClassCastException(context.toString() + " must implement UrlHistoryListener.");
-        }
-    }
+    @NonNull
+    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    @SuppressLint("InflateParams")
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Remove the incorrect lint warning that `getActivity()` might be null.
+        assert getActivity() != null;
 
+        // Get the activity's layout inflater.
+        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
 
-    public static UrlHistoryDialog loadBackForwardList(Context context, WebBackForwardList webBackForwardList) {
-        // Create an arguments bundle.
-        Bundle argumentsBundle = new Bundle();
+        // Get the arguments.
+        Bundle arguments = getArguments();
+
+        // Remove the incorrect lint error that arguments might be null.
+        assert arguments != null;
+
+        // Get the WebView fragment ID from the arguments.
+        long webViewFragmentId = arguments.getLong("webview_fragment_id");
+
+        // Get the current position of this WebView fragment.
+        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
+
+        // Get the WebView tab fragment.
+        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
+
+        // Get the fragment view.
+        View fragmentView = webViewTabFragment.getView();
+
+        // Remove the incorrect lint warning below that the fragment view might be null.
+        assert fragmentView != null;
+
+        // Get a handle for the current WebView.
+        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+        // Get the web back forward list from the WebView.
+        WebBackForwardList webBackForwardList = nestedScrollWebView.copyBackForwardList();
 
         // Store the current page index.
         int currentPageIndex = webBackForwardList.getCurrentIndex();
 
-        // Setup the URL array list and the icon array list.
-        ArrayList<String> urlArrayList = new ArrayList<>();
-        ArrayList<String> iconBase64StringArrayList = new ArrayList<>();
+        // Remove the lint warning below that `getContext()` might be null.
+        assert getContext() != null;
 
         // Get the default favorite icon drawable.  `ContextCompat` must be used until the minimum API >= 21.
-        Drawable defaultFavoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world);
+        Drawable defaultFavoriteIconDrawable = ContextCompat.getDrawable(getContext(), R.drawable.world);
 
         // Convert the default favorite icon drawable to a `BitmapDrawable`.
         BitmapDrawable defaultFavoriteIconBitmapDrawable = (BitmapDrawable) defaultFavoriteIconDrawable;
@@ -101,102 +120,34 @@ public class UrlHistoryDialog extends DialogFragment{
         // Remove the incorrect lint error that `getBitmap()` might be null.
         assert defaultFavoriteIconBitmapDrawable != null;
 
-        // Extract a `Bitmap` from the default favorite icon `BitmapDrawable`.
+        // Extract a bitmap from the default favorite icon bitmap drawable.
         Bitmap defaultFavoriteIcon = defaultFavoriteIconBitmapDrawable.getBitmap();
 
-        // Populate the URL array list and the icon array list from `webBackForwardList`.
-        for (int i=0; i < webBackForwardList.getSize(); i++) {
-            // Store the URL.
-            urlArrayList.add(webBackForwardList.getItemAtIndex(i).getUrl());
+        // Create a history array list.
+        ArrayList<History> historyArrayList = new ArrayList<>();
 
-            // Create a variable to store the icon bitmap.
-            Bitmap iconBitmap;
+        // Populate the history array list, descending from `urlStringArrayList.size()` so that the newest entries are at the top.  `-1` is needed because the history array list is zero-based.
+        for (int i=webBackForwardList.getSize() -1; i >= 0; i--) {
+            // Create a variable to store the favorite icon bitmap.
+            Bitmap favoriteIconBitmap;
 
-            // Store the icon bitmap.
+            // Determine the favorite icon bitmap
             if (webBackForwardList.getItemAtIndex(i).getFavicon() == null) {
-                // If `webBackForwardList` does not have a favorite icon, use Privacy Browser's default world icon.
-                iconBitmap = defaultFavoriteIcon;
-            } else {  // Get the icon from `webBackForwardList`.
-                iconBitmap = webBackForwardList.getItemAtIndex(i).getFavicon();
+                // If the web back forward list does not have a favorite icon, use Privacy Browser's default world icon.
+                favoriteIconBitmap = defaultFavoriteIcon;
+            } else {  // Use the icon from the web back forward list.
+                favoriteIconBitmap = webBackForwardList.getItemAtIndex(i).getFavicon();
             }
 
-            // Create a `ByteArrayOutputStream`.
-            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Remove the incorrect lint error that `compress()` might be null;
-            assert iconBitmap != null;
-
-            // Convert the favorite icon `Bitmap` to a `ByteArrayOutputStream`.  `100` is the compression quality, which is ignored by `PNG`.
-            iconBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
-
-            // Convert the favorite icon `ByteArrayOutputStream` to a `byte[]`.
-            byte[] byteArray = byteArrayOutputStream.toByteArray();
-
-            // Encode the favorite icon `byte[]` as a Base64 `String`.
-            String iconBase64String = Base64.encodeToString(byteArray, Base64.DEFAULT);
-
-            // Store the favorite icon Base64 `String` in `iconBase64StringArrayList`.
-            iconBase64StringArrayList.add(iconBase64String);
-        }
-
-        // Store the variables in the `Bundle`.
-        argumentsBundle.putInt("Current_Page", currentPageIndex);
-        argumentsBundle.putStringArrayList("URL_History", urlArrayList);
-        argumentsBundle.putStringArrayList("Favorite_Icons", iconBase64StringArrayList);
-
-        // Add the arguments bundle to this instance of `UrlHistoryDialog`.
-        UrlHistoryDialog thisUrlHistoryDialog = new UrlHistoryDialog();
-        thisUrlHistoryDialog.setArguments(argumentsBundle);
-        return thisUrlHistoryDialog;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Remove the incorrect lint error that `getArguments()` might be null.
-        assert getArguments() != null;
+            // Store the favorite icon and the URL in history entry.
+            History historyEntry = new History(favoriteIconBitmap, webBackForwardList.getItemAtIndex(i).getUrl());
 
-        // Get the `ArrayLists` from the `Arguments`.
-        ArrayList<String> urlStringArrayList = getArguments().getStringArrayList("URL_History");
-        ArrayList<String> favoriteIconBase64StringArrayList = getArguments().getStringArrayList("Favorite_Icons");
-
-        // Remove the lint warning below that the `ArrayLists` might be `null`.
-        assert urlStringArrayList != null;
-        assert favoriteIconBase64StringArrayList != null;
-
-        // Populate `historyArrayList`.  We go down from `urlStringArrayList.size()` so that the newest entries are at the top.  `-1` is needed because `historyArrayList` is zero-based.
-        for (int i=urlStringArrayList.size() -1; i >= 0; i--) {
-            // Decode the favorite icon Base64 `String` to a `byte[]`.
-            byte[] favoriteIconByteArray = Base64.decode(favoriteIconBase64StringArrayList.get(i), Base64.DEFAULT);
-
-            // Convert the favorite icon `byte[]` to a `Bitmap`.  `0` is the starting offset.
-            Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-            // Store the favorite icon and the URL in `historyEntry`.
-            History historyEntry = new History(favoriteIconBitmap, urlStringArrayList.get(i));
-
-            // Add this history entry to `historyArrayList`.
+            // Add this history entry to the history array list.
             historyArrayList.add(historyEntry);
         }
 
-        // Get the original current page ID.
-        int originalCurrentPageId = getArguments().getInt("Current_Page");
-
-        // Subtract `originalCurrentPageId` from the array size because we reversed the order of the array so that the newest entries are at the top.  `-1` is needed because the array is zero-based.
-        currentPageId = urlStringArrayList.size() - 1 - originalCurrentPageId;
-    }
-
-    @Override
-    @NonNull
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-    @SuppressLint("InflateParams")
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Remove the incorrect lint warning that `getActivity()` might be null.
-        assert getActivity() != null;
-
-        // Get the activity's layout inflater.
-        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
+        // Subtract the original current page ID from the array size because the order of the array is reversed so that the newest entries are at the top.  `-1` is needed because the array is zero-based.
+        int currentPageId = webBackForwardList.getSize() - 1 - currentPageIndex;
 
         // Use an alert dialog builder to create the alert dialog.
         AlertDialog.Builder dialogBuilder;
@@ -221,10 +172,10 @@ public class UrlHistoryDialog extends DialogFragment{
         // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
         dialogBuilder.setView(layoutInflater.inflate(R.layout.url_history_dialog, null));
 
-        // Set an `onClick()` listener on the negative button.
+        // Setup the clear history button.
         dialogBuilder.setNegativeButton(R.string.clear_history, (DialogInterface dialog, int which) -> {
             // Clear the history.
-            urlHistoryListener.onClearHistory();
+            nestedScrollWebView.clearHistory();
         });
 
         // Set an `onClick()` listener on the positive button.
@@ -247,7 +198,7 @@ public class UrlHistoryDialog extends DialogFragment{
         //The alert dialog must be shown before the contents can be modified.
         alertDialog.show();
 
-        // Instantiate a `HistoryArrayAdapter`.
+        // Instantiate a history array adapter.
         HistoryArrayAdapter historyArrayAdapter = new HistoryArrayAdapter(getContext(), historyArrayList, currentPageId);
 
         // Get a handle for the list view.
@@ -263,15 +214,21 @@ public class UrlHistoryDialog extends DialogFragment{
 
             // Only consume the click if it is not on the `currentPageId`.
             if (itemId != currentPageId) {
-                // Go forward or back to `itemId`.
-                urlHistoryListener.onUrlHistoryEntrySelected(currentPageId - itemId);
+                // Reset the current domain name so that navigation works if third-party requests are blocked.
+                nestedScrollWebView.resetCurrentDomainName();
+
+                // Set navigating history so that the domain settings are applied when the new URL is loaded.
+                nestedScrollWebView.setNavigatingHistory(true);
+
+                // Load the history entry.
+                nestedScrollWebView.goBackOrForward(currentPageId - itemId);
 
-                // Dismiss the `Dialog`.
+                // Dismiss the alert dialog.
                 alertDialog.dismiss();
             }
         });
 
-        // `onCreateDialog` requires the return of an `AlertDialog`.
+        // Return the alert dialog.
         return alertDialog;
     }
 }
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_tab_view.xml b/app/src/main/res/layout/custom_tab_view.xml
deleted file mode 100644 (file)
index c23a23e..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  Copyright © 2015-2017,2019 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/>. -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_height="match_parent"
-    android:layout_width="wrap_content"
-    android:orientation="horizontal"
-    tools:ignore="UseCompoundDrawables" >
-
-    <ImageView
-        android:id="@+id/favorite_icon_imageview"
-        android:src="@drawable/world"
-        android:layout_height="26dp"
-        android:layout_width="26dp"
-        android:layout_gravity="center_vertical"
-        android:layout_marginEnd="5dp"
-        android:contentDescription="@string/favorite_icon" />
-
-    <TextView
-        android:id="@+id/title_textview"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:gravity="center_vertical"
-        android:textSize="16sp"
-        android:text="@string/new_tab"
-        app:autoSizeTextType="uniform"
-        android:maxWidth="150dp"
-        android:maxLines="2"
-        android:ellipsize="end" />
-</LinearLayout>
\ No newline at end of file
index 645c453c8c341f3aca19c2b6683079d185e46ebe..b47501c91aa796f5114b4cd562040796bc8f83c4 100644 (file)
@@ -23,6 +23,7 @@ When it is specified the theme should include <item name="android:windowTransluc
 <androidx.coordinatorlayout.widget.CoordinatorLayout
     android:id="@+id/guide_coordinatorlayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:android.support.design="http://schemas.android.com/apk/res-auto"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:fitsSystemWindows="true" >
@@ -51,7 +52,6 @@ When it is specified the theme should include <item name="android:windowTransluc
             <!-- For some reason `tabIndicatorColor` does not pull from the style unless specified explicitly here. -->
             <com.google.android.material.tabs.TabLayout
                 android:id="@+id/guide_tablayout"
-                xmlns:android.support.design="http://schemas.android.com/apk/res-auto"
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
                 android.support.design:tabMode="scrollable"
index 3057c9220f4c7f82817ce313b662b3d19d251dbe..a5ed4bf8289c6b61a9d06319cdb313a3b9a4f764 100644 (file)
@@ -20,7 +20,7 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:android.support.design="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/root_framelayout"
     android:layout_height="match_parent"
                         android:id="@+id/toolbar"
                         android:layout_height="wrap_content"
                         android:layout_width="match_parent"
-                        app:layout_scrollFlags="scroll|enterAlways|snap" />
+                        android.support.design:layout_scrollFlags="scroll|enterAlways|snap" />
 
-                    <HorizontalScrollView
+                    <LinearLayout
                         android:layout_height="wrap_content"
-                        android:layout_width="match_parent"
-                        app:layout_scrollFlags="scroll|enterAlways|snap" >
+                        android:layout_width="wrap_content"
+                        android:orientation="horizontal"
+                        android.support.design:layout_scrollFlags="scroll|enterAlways|snap" >
+
+                        <com.google.android.material.tabs.TabLayout
+                            android:id="@+id/tablayout"
+                            android:layout_height="wrap_content"
+                            android:layout_width="0dp"
+                            android:layout_weight="1"
+                            android.support.design:tabIndicatorGravity="top"
+                            android.support.design:tabMode="scrollable" />
 
-                        <LinearLayout
+                        <ImageView
                             android:layout_height="wrap_content"
                             android:layout_width="wrap_content"
-                            android:orientation="horizontal" >
-
-                            <com.google.android.material.tabs.TabLayout
-                                android:id="@+id/tablayout"
-                                android:layout_height="wrap_content"
-                                android:layout_width="wrap_content"
-                                app:tabIndicatorGravity="top"
-                                app:tabMode="scrollable" />
-
-                            <!-- `style="?android:borderlessButtonStyle"` shows a visual indication when the image view is tapped. -->
-                            <ImageView
-                                android:layout_height="wrap_content"
-                                android:layout_width="wrap_content"
-                                android:layout_gravity="center_vertical"
-                                android:src="@drawable/add_light"
-                                android:tint="?attr/addTabIconTintColor"
-                                android:onClick="addTab"
-                                android:contentDescription="@string/add_tab"
-                                style="?android:attr/borderlessButtonStyle" />
-                        </LinearLayout>
-                    </HorizontalScrollView>
+                            android:layout_gravity="center_vertical"
+                            android:paddingStart="10dp"
+                            android:paddingEnd="10dp"
+                            android:src="@drawable/add_light"
+                            android:tint="?attr/addTabIconTintColor"
+                            android:onClick="addTab"
+                            android:contentDescription="@string/add_tab" />
+                    </LinearLayout>
 
                     <!-- The find on page linear layout.  It is initially `visibility="gone"`. -->
                     <LinearLayout
                     android:id="@+id/swiperefreshlayout"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
-                    app:layout_behavior="@string/appbar_scrolling_view_behavior" >
+                    android.support.design:layout_behavior="@string/appbar_scrolling_view_behavior" >
 
                         <androidx.viewpager.widget.ViewPager
                             android:id="@+id/webviewpager"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
             android:layout_gravity="start"
-            app:headerLayout="@layout/navigation_header"
-            app:menu="@menu/webview_navigation_menu"
-            app:itemIconTint="?attr/navigationIconTintColor" />
+            android.support.design:headerLayout="@layout/navigation_header"
+            android.support.design:menu="@menu/webview_navigation_menu"
+            android.support.design:itemIconTint="?attr/navigationIconTintColor" />
 
         <!-- Include the bookmarks drawer, which varies based on screen width. -->
         <include layout="@layout/bookmarks_drawer" />
diff --git a/app/src/main/res/layout/tab_custom_view.xml b/app/src/main/res/layout/tab_custom_view.xml
new file mode 100644 (file)
index 0000000..17085e4
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2015-2017,2019 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/>. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content"
+    android:orientation="horizontal"
+    tools:ignore="UseCompoundDrawables">
+
+    <ImageView
+        android:id="@+id/favorite_icon_imageview"
+        android:src="@drawable/world"
+        android:layout_height="26dp"
+        android:layout_width="26dp"
+        android:layout_gravity="center_vertical"
+        android:layout_marginEnd="5dp"
+        android:contentDescription="@string/favorite_icon" />
+
+    <TextView
+        android:id="@+id/title_textview"
+        android:layout_height="match_parent"
+        android:layout_width="120dp"
+        android:gravity="center_vertical"
+        android:text="@string/new_tab"
+        android:maxLines="2"
+        android:ellipsize="end" />
+</LinearLayout>
\ No newline at end of file
index 63641a11673b1b2d64ea5a9efeb9d4645601c838..1daae7692b9aad4d5deccec9eb6aafa080cc3c9c 100644 (file)
@@ -76,8 +76,8 @@
         <item name="sortIcon">@drawable/sort_light</item>
         <item name="actionBarPopupTheme">@style/PrivacyBrowserPopupsLight</item>
         <item name="appBarTextTheme">@style/PrivacyBrowserAppBarWhiteText</item>
-        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutLight</item>
         <item name="popupsTheme">@style/PrivacyBrowserPopupsLight</item>
+        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutLight</item>
     </style>
 
     <!-- `colorPrimaryDark` is the color of the status bar. -->
         <item name="moveToFolderIcon">@drawable/move_to_folder_dark</item>
         <item name="saveIcon">@drawable/save_dark</item>
         <item name="sortIcon">@drawable/sort_dark</item>
+        <item name="android:spinnerDropDownItemStyle">@style/PrivacyBrowserSpinnerDropDownItemStyleDark</item>
         <item name="appBarTextTheme">@style/PrivacyBrowserAppBarDark</item>
-        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutDark</item>
         <item name="popupsTheme">@style/PrivacyBrowserPopupsDark</item>
-        <item name="android:spinnerDropDownItemStyle">@style/PrivacyBrowserSpinnerDropDownItemStyleDark</item>
+        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutDark</item>
     </style>
 
     <style name="PrivacyBrowserSettingsDark" parent="Theme.AppCompat" >
index d7e3f21ba9835a7253e42dd4602b9b9d569d6115..147a7155729e157d2fb4328fab1614fb23a4c51e 100644 (file)
@@ -53,6 +53,7 @@
     <string name="open_with">Open with</string>
     <string name="new_tab">New tab</string>
     <string name="add_tab">Add tab</string>
+    <string name="loading">Loading…</string>
 
     <!-- Save As. -->
     <string name="save_as">Save As</string>
index 26e0c8931d4d528bd26087a28a6368ed3a524518..c8af48ff513778ded045e3788e1ed137df506c4a 100644 (file)
@@ -74,8 +74,8 @@
         <item name="sortIcon">@drawable/sort_light</item>
         <item name="actionBarPopupTheme">@style/PrivacyBrowserPopupsLight</item>
         <item name="appBarTextTheme">@style/PrivacyBrowserAppBarWhiteText</item>
-        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutLight</item>
         <item name="popupsTheme">@style/PrivacyBrowserPopupsLight</item>
+        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutLight</item>
     </style>
 
     <!-- `colorPrimaryDark` is the color of the status bar. -->
         <item name="moveToFolderIcon">@drawable/move_to_folder_dark</item>
         <item name="saveIcon">@drawable/save_dark</item>
         <item name="sortIcon">@drawable/sort_dark</item>
+        <item name="android:spinnerDropDownItemStyle">@style/PrivacyBrowserSpinnerDropDownItemStyleDark</item>
         <item name="appBarTextTheme">@style/PrivacyBrowserAppBarDark</item>
-        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutDark</item>
         <item name="popupsTheme">@style/PrivacyBrowserPopupsDark</item>
-        <item name="android:spinnerDropDownItemStyle">@style/PrivacyBrowserSpinnerDropDownItemStyleDark</item>
+        <item name="tabLayoutTheme">@style/PrivacyBrowserTabLayoutDark</item>
     </style>
 
     <style name="PrivacyBrowserSettingsDark" parent="Theme.AppCompat" >