Fix a crash when opening data:image URL's with an external browser. https://redmine...
authorSoren Stoutner <soren@stoutner.com>
Thu, 27 Jun 2019 04:56:22 +0000 (21:56 -0700)
committerSoren Stoutner <soren@stoutner.com>
Thu, 27 Jun 2019 04:56:22 +0000 (21:56 -0700)
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/res/values-de/strings.xml
app/src/main/res/values-es/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values-ru/strings.xml
app/src/main/res/values-tr/strings.xml
app/src/main/res/values/strings.xml

index 8ae5e7add4755185260d7bc8a134736fcad66a36..af6f05c4ca57691d395edc18471db3a9e220ec71 100644 (file)
@@ -50,6 +50,7 @@ import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.Message;
 import android.preference.PreferenceManager;
 import android.print.PrintDocumentAdapter;
 import android.print.PrintManager;
@@ -2000,7 +2001,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Store the hit test result.
         final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
 
-        // Create the URL strings.
+        // Define the URL strings.
         final String imageUrl;
         final String linkUrl;
 
@@ -2026,19 +2027,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
                     // Load the link URL in a new tab.
                     addNewTab(linkUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add an Open with App entry.
                 menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
                     openWithApp(linkUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add an Open with Browser entry.
                 menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> {
                     openWithBrowser(linkUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a Copy URL entry.
@@ -2048,7 +2055,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Set the `ClipData` as the clipboard's primary clip.
                     clipboardManager.setPrimaryClip(srcAnchorTypeClipData);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a Download URL entry.
@@ -2083,7 +2092,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             downloadFileDialogFragment.show(fragmentManager, getString(R.string.download));
                         }
                     }
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a Cancel entry, which by default closes the context menu.
@@ -2110,7 +2121,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Make it so.
                     startActivity(emailIntent);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a Copy Email Address entry.
@@ -2120,16 +2133,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Set the `ClipData` as the clipboard's primary clip.
                     clipboardManager.setPrimaryClip(srcEmailTypeClipData);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a `Cancel` entry, which by default closes the `ContextMenu`.
                 menu.add(R.string.cancel);
                 break;
 
-            // `IMAGE_TYPE` is an image. `SRC_IMAGE_ANCHOR_TYPE` is an image that is also a link.  Privacy Browser processes them the same.
+            // `IMAGE_TYPE` is an image.
             case WebView.HitTestResult.IMAGE_TYPE:
-            case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                 // Get the image URL.
                 imageUrl = hitTestResult.getExtra();
 
@@ -2140,16 +2154,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
                     // Load the image URL in a new tab.
                     addNewTab(imageUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add a View Image entry.
                 menu.add(R.string.view_image).setOnMenuItemClickListener(item -> {
+                    // Load the image in the current tab.
                     loadUrl(imageUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
-                // Add a `Download Image` entry.
+                // Add a Download Image entry.
                 menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Check if the download should be processed by an external app.
                     if (sharedPreferences.getBoolean("download_with_external_app", false)) {  // Download with an external app.
@@ -2168,7 +2187,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                 // Show the download location permission alert dialog.  The permission will be requested when the dialog is closed.
                                 downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location));
                             } else {  // Show the permission request directly.
-                                // Request the permission.  The download dialog will be launched by `onRequestPermissionResult().
+                                // Request the permission.  The download dialog will be launched by `onRequestPermissionResult()`.
                                 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE);
                             }
                         } else {  // The storage permission has already been granted.
@@ -2179,32 +2198,149 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             downloadImageDialogFragment.show(fragmentManager, getString(R.string.download));
                         }
                     }
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
-                // Add a `Copy URL` entry.
-                menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> {
-                    // Save the image URL in a `ClipData`.
-                    ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
+                // Add a Copy URL entry.
+                menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Save the image URL in a clip data.
+                    ClipData imageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
+
+                    // Set the clip data as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(imageTypeClipData);
 
-                    // Set the `ClipData` as the clipboard's primary clip.
-                    clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
-                    return false;
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add an Open with App entry.
                 menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Open the image URL with an external app.
                     openWithApp(imageUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
                 // Add an Open with Browser entry.
                 menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Open the image URL with an external browser.
                     openWithBrowser(imageUrl);
-                    return false;
+
+                    // Consume the event.
+                    return true;
                 });
 
-                // Add a `Cancel` entry, which by default closes the `ContextMenu`.
+                // Add a Cancel entry, which by default closes the context menu.
+                menu.add(R.string.cancel);
+                break;
+
+            // `SRC_IMAGE_ANCHOR_TYPE` is an image that is also a link.
+            case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
+                // Get the image URL.
+                imageUrl = hitTestResult.getExtra();
+
+                // Instantiate a handler.
+                Handler handler = new Handler();
+
+                // Get a message from the handler.
+                Message message = handler.obtainMessage();
+
+                // Request the image details from the last touched node be returned in the message.
+                currentWebView.requestFocusNodeHref(message);
+
+                // Get the link URL from the message data.
+                linkUrl = message.getData().getString("url");
+
+                // Set the link URL as the title of the context menu.
+                menu.setHeaderTitle(linkUrl);
+
+                // Add an Open in New Tab entry.
+                menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Load the link URL in a new tab.
+                    addNewTab(linkUrl);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add a View Image entry.
+                menu.add(R.string.view_image).setOnMenuItemClickListener((MenuItem item) -> {
+                   // View the image in the current tab.
+                   loadUrl(imageUrl);
+
+                   // Consume the event.
+                   return true;
+                });
+
+                // Add a Download Image entry.
+                menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Check if the download should be processed by an external app.
+                    if (sharedPreferences.getBoolean("download_with_external_app", false)) {  // Download with an external app.
+                        openUrlWithExternalApp(imageUrl);
+                    } else {  // Download with Android's download manager.
+                        // Check to see if the storage permission has already been granted.
+                        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {  // The storage permission needs to be requested.
+                            // Store the image URL for use by `onRequestPermissionResult()`.
+                            downloadImageUrl = imageUrl;
+
+                            // Show a dialog if the user has previously denied the permission.
+                            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
+                                // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE.
+                                DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE);
+
+                                // Show the download location permission alert dialog.  The permission will be requested when the dialog is closed.
+                                downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location));
+                            } else {  // Show the permission request directly.
+                                // Request the permission.  The download dialog will be launched by `onRequestPermissionResult()`.
+                                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE);
+                            }
+                        } else {  // The storage permission has already been granted.
+                            // Get a handle for the download image alert dialog.
+                            DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
+
+                            // Show the download image alert dialog.
+                            downloadImageDialogFragment.show(fragmentManager, getString(R.string.download));
+                        }
+                    }
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add a Copy URL entry.
+                menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Save the link URL in a clip data.
+                    ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
+
+                    // Set the clip data as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add an Open with App entry.
+                menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Open the link URL with an external app.
+                    openWithApp(linkUrl);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add an Open with Browser entry.
+                menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Open the link URL with an external browser.
+                    openWithBrowser(linkUrl);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add a cancel entry, which by default closes the context menu.
                 menu.add(R.string.cancel);
                 break;
         }
@@ -4216,8 +4352,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Flag the intent to open in a new task.
         openWithAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        // Show the chooser.
-        startActivity(openWithAppIntent);
+        try {
+            // Show the chooser.
+            startActivity(openWithAppIntent);
+        } catch (ActivityNotFoundException exception) {
+            // Show a snackbar with the error.
+            Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+        }
     }
 
     private void openWithBrowser(String url) {
@@ -4230,8 +4371,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Flag the intent to open in a new task.
         openWithBrowserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        // Show the chooser.
-        startActivity(openWithBrowserIntent);
+        try {
+            // Show the chooser.
+            startActivity(openWithBrowserIntent);
+        } catch (ActivityNotFoundException exception) {
+            // Show a snackbar with the error.
+            Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+        }
     }
 
     private String sanitizeUrl(String url) {
index 9fc59d17b79f5eb863e8a24dfa64c0b533d8bcb8..a24f978cad7aa6cc9f126cab7d2f182e2b0efe8a 100644 (file)
         <string name="find_on_page">Auf Seite finden</string>
         <string name="print">Drucken</string>
             <string name="privacy_browser_web_page">Privacy Browser-Website</string>
+        <string name="save_as_image">Als Grafik speichern</string>
         <string name="view_source">Quelltext anzeigen</string>
         <string name="add_to_home_screen">Zur Startseite hinzufügen</string>
     <string name="share">Teilen</string>
     <string name="previous">Vorheriges</string>
     <string name="next">Nächstes</string>
 
+    <!-- Save Webpage as Image. -->
+    <string name="save_image">Grafik speichern</string>
+    <string name="webpage_png">Webseite.png</string>
+    <string name="saving_image">Speichere Grafik…</string>
+    <string name="image_saved">Grafik gespeichert.</string>
+    <string name="error_saving_image">Fehler beim Speichern der Grafik:</string>
+
     <!-- View Source. -->
     <string name="request_headers">Anfragekopfzeilen</string>
     <string name="response_message">Status-Code</string>
index fd14e0cfd5eae7a6e4a7af1c9672246cec5110bb..c475ead2883acacf6d6f76bc1a1f157f1109146a 100644 (file)
         <string name="display_images">Mostrar imágenes</string>
         <string name="print">Imprimir</string>
             <string name="privacy_browser_web_page">Página web de Navegador Privado</string>
+        <string name="save_as_image">Guardar como imagen</string>
         <string name="add_to_home_screen">Añadir a la ventana de inicio</string>
         <string name="options_night_mode">Modo noche</string>
     <string name="find_on_page">Buscar en página</string>
     <string name="previous">Anterior</string>
     <string name="next">Siguiente</string>
 
+    <!-- Save Webpage as Image. -->
+    <string name="save_image">Guardar imagen</string>
+    <string name="webpage_png">Webpage.png</string>
+    <string name="saving_image">Guardando imagen…</string>
+    <string name="image_saved">Imagen guardada.</string>
+    <string name="error_saving_image">Error guardando imagen:</string>
+
     <!-- View Source. -->
     <string name="request_headers">Cabeceras de solicitud</string>
     <string name="response_message">Mensaje de respuesta</string>
index 444ee8a58785be205a9140d12a2e7f40fd5e24f0..0aaa07a04ec79ba26e1f024e71c963cfd8c18cf6 100644 (file)
         <string name="find_on_page">Cerca nella pagina</string>
         <string name="print">Stampa</string>
             <string name="privacy_browser_web_page">Pagina web di Privacy Browser</string>
+        <string name="save_as_image">Salva come Immagine</string>
         <string name="add_to_home_screen">Aggiungi collegamento</string>
         <string name="view_source">Visualizza sorgente</string>
     <string name="share">Condividi</string>
     <string name="previous">Precedente</string>
     <string name="next">Successivo</string>
 
+    <!-- Save Webpage as Image. -->
+    <string name="save_image">Salva Immagine</string>
+    <string name="webpage_png">Webpage.png</string>
+    <string name="saving_image">Salvataggio immagine…</string>
+    <string name="image_saved">Immagine salvata.</string>
+    <string name="error_saving_image">Errore nel salvare l\'immagine:</string>
+
     <!-- View Source. -->
     <string name="request_headers">Richiesta Intestazioni</string>
     <string name="response_message">Messaggio di Risposta</string>
index 35e3a4b7f13944d1e122c8704c0f36c198de61b1..51feed83e0332cbdc507d2caf160e44325a00d9b 100644 (file)
         <string name="find_on_page">Найти на странице</string>
         <string name="print">Печать</string>
             <string name="privacy_browser_web_page">Privacy Browser веб-страница</string>
+        <string name="save_as_image">Сохранить как изображение</string>
         <string name="add_to_home_screen">Добавить на главный экран</string>
         <string name="view_source">Просмотр исходного кода</string>
     <string name="share">Поделиться</string>
     <string name="previous">Предыдущий</string>
     <string name="next">Следующий</string>
 
+    <!-- Save Webpage as Image. -->
+    <string name="save_image">Сохранить изображение</string>
+    <string name="webpage_png">Webpage.png</string>
+    <string name="saving_image">Сохранение изображения…</string>
+    <string name="image_saved">Изображение сохранено.</string>
+    <string name="error_saving_image">Ошибка сохранения изображения:</string>
+
     <!-- View Source. -->
     <string name="request_headers">Заголовки запроса</string>
     <string name="response_message">Ответное сообщение</string>
index ebb1189f7c9d9e09323d09ec07d341bab5fe744e..a429ebbea022d7af874444900866ce3b80078266 100644 (file)
         <string name="find_on_page">Sayfada bul</string>
         <string name="print">Yazdır</string>
             <string name="privacy_browser_web_page">Privacy Browser Web Sayfası</string>
+        <string name="save_as_image">Resmi farklı kaydet</string>
         <string name="add_to_home_screen">Ana ekrana ekle</string>
         <string name="view_source">Kaynağı görüntüle</string>
     <string name="share">Paylaş</string>
     <string name="previous">Önceki</string>
     <string name="next">Sonraki</string>
 
+    <!-- Save Webpage as Image. -->
+    <string name="save_image">Resmi kaydet</string>
+    <string name="webpage_png">Websayfası.png</string>
+    <string name="saving_image">Resim kaydediliyor…</string>
+    <string name="image_saved">Resim kaydedildi</string>
+    <string name="error_saving_image">Resim kaydı başarısız:</string>
+
     <!-- View Source. -->
     <string name="request_headers">İstek Başlıkları</string>
     <string name="response_message">Yanıt Mesajı</string>
index 1f8996886aede0e29118b419dffe49d530e066f8..fdc58f4aa76d57672968d4a52a2596be4977d63d 100644 (file)
@@ -55,6 +55,7 @@
     <string name="close_tab">Close Tab</string>
     <string name="new_tab">New tab</string>
     <string name="loading">Loading…</string>
+    <string name="error">Error:</string>
 
     <!-- Loading Blocklists. -->
     <string name="loading_easylist">Loading EasyList</string>