From 047f37ce55e2299817d187f5128247eaa2d653dd Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Mon, 25 Feb 2019 12:52:01 -0700 Subject: [PATCH] Add the URL to the create homescreen shortcut dialog. https://redmine.stoutner.com/issues/334 --- .../activities/MainWebViewActivity.java | 49 +--- .../CreateHomeScreenShortcutDialog.java | 242 +++++++++++++++--- .../create_home_screen_shortcut_dialog.xml | 29 ++- app/src/main/res/values-es/strings.xml | 4 + app/src/main/res/values-ru/strings.xml | 4 + 5 files changed, 248 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index c67d151c..98b2540a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -100,10 +100,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat versions once API >= 26. -import androidx.core.content.pm.ShortcutInfoCompat; -import androidx.core.content.pm.ShortcutManagerCompat; -import androidx.core.graphics.drawable.IconCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.DialogFragment; @@ -159,9 +155,8 @@ import java.util.Set; // 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, - CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, - DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, - HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, + DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, + EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener { // `darkTheme` is public static so it can be accessed from everywhere. @@ -172,13 +167,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, // `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`. It is also used in `onCreate()`, - // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`. + // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcut()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`. public static Bitmap favoriteIconBitmap; // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()` and `applyDomainSettings`. public static Bitmap favoriteIconDefaultBitmap; - // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, `CreateBookmarkDialog`, and `PinnedMismatchDialog`. + // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, `CreateBookmarkDialog`, + // and `PinnedMismatchDialog`. // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`. public static String formattedUrlString; @@ -193,7 +189,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. public static String orbotStatus; - // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`. + // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog`. It is also used in `onCreate()`. public static String webViewTitle; // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`. @@ -2874,11 +2870,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.add_to_homescreen: - // Show the alert dialog. - DialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); - createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); + // Instantiate the create home screen shortcut dialog. + DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(mainWebView.getTitle(), formattedUrlString, favoriteIconBitmap); - //Everything else will be handled by the alert dialog and the associated listener below. + // Show the create home screen shortcut dialog. + createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); return true; case R.id.proxy_through_orbot: @@ -3563,31 +3559,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook bookmarksListView.setSelection(0); } - @Override - public void onCreateHomeScreenShortcut(DialogFragment dialogFragment) { - // Get the shortcut name. - EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext); - String shortcutNameString = shortcutNameEditText.getText().toString(); - - // Convert the favorite icon bitmap to an `Icon`. `IconCompat` is required until API >= 26. - IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap); - - // Setup the shortcut intent. - Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); - shortcutIntent.setData(Uri.parse(formattedUrlString)); - - // Create a shortcut info builder. The shortcut name becomes the shortcut ID. - ShortcutInfoCompat.Builder shortcutInfoBuilder = new ShortcutInfoCompat.Builder(this, shortcutNameString); - - // Add the required fields to the shortcut info builder. - shortcutInfoBuilder.setIcon(favoriteIcon); - shortcutInfoBuilder.setIntent(shortcutIntent); - shortcutInfoBuilder.setShortLabel(shortcutNameString); - - // Request the pin. `ShortcutManagerCompat` can be switched to `ShortcutManager` once API >= 26. - ShortcutManagerCompat.requestPinShortcut(this, shortcutInfoBuilder.build(), null); - } - @Override public void onCloseDownloadLocationPermissionDialog(int downloadType) { switch (downloadType) { diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.java index cb9accc6..32cd5fac 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.java @@ -24,37 +24,94 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import android.widget.Button; import android.widget.EditText; import androidx.annotation.NonNull; +// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat versions once API >= 26. +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.DialogFragment; // The AndroidX dialog fragment must be used or an error is produced on API <=22. import com.stoutner.privacybrowser.activities.MainWebViewActivity; import com.stoutner.privacybrowser.R; +import java.io.ByteArrayOutputStream; + public class CreateHomeScreenShortcutDialog extends DialogFragment { - // The public interface is used to send information back to the parent activity. - public interface CreateHomeScreenShortcutListener { - void onCreateHomeScreenShortcut(DialogFragment dialogFragment); - } + // Create the class variables. + private String initialShortcutName; + private String initialUrlString; + private Bitmap favoriteIconBitmap; + private EditText shortcutNameEditText; + private EditText urlEditText; + private Button createButton; + + public static CreateHomeScreenShortcutDialog createDialog(String shortcutName, String urlString, Bitmap favoriteIconBitmap) { + // Create a favorite icon byte array output stream. + ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the favorite icon to a PNG and place it in the byte array output stream. `0` is for lossless compression (the only option for a PNG). + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the byte array output stream to a byte array. + byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); + + // Create a bundle. + Bundle bundle = new Bundle(); + + // Store the variables in the bundle. + bundle.putString("shortcut_name", shortcutName); + bundle.putString("url_string", urlString); + bundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray); - //`createHomeScreenShortcutListener` is used in `onAttach()` and `onCreateDialog()`. - private CreateHomeScreenShortcutListener createHomeScreenShortcutListener; + // Create a new instance of the dialog. + CreateHomeScreenShortcutDialog createHomeScreenShortcutDialog = new CreateHomeScreenShortcutDialog(); - // Check to make sure that the parent activity implements the listener. - public void onAttach(Context context) { + // Add the bundle to the dialog. + createHomeScreenShortcutDialog.setArguments(bundle); + + // Return the new dialog. + return createHomeScreenShortcutDialog; + } + + @Override + public void onCreate(Bundle savedInstanceState) { // Run the default commands. - super.onAttach(context); + super.onCreate(savedInstanceState); + + // Get the arguments. + Bundle arguments = getArguments(); + + // Remove the incorrect lint warning that the arguments might be null. + assert arguments != null; - // Get a handle for `CreateHomeScreenShortcutListener` from the launching context. - createHomeScreenShortcutListener = (CreateHomeScreenShortcutListener) context; + // Store the strings in class variables. + initialShortcutName = arguments.getString("shortcut_name"); + initialUrlString = arguments.getString("url_string"); + + // Get the favorite icon byte array. + byte[] favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array"); + + // Remove the incorrect lint warning below that the favorite icon byte array might be null. + assert favoriteIconByteArray != null; + + // Convert the favorite icon byte array to a bitmap and store it in a class variable. + favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); } // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`. @@ -69,9 +126,9 @@ public class CreateHomeScreenShortcutDialog extends DialogFragment { LayoutInflater layoutInflater = getActivity().getLayoutInflater(); // Create a drawable version of the favorite icon. - Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap); + Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), favoriteIconBitmap); - // Use `AlertDialog.Builder` to create the `AlertDialog`. + // Use a builder to create the alert dialog. AlertDialog.Builder dialogBuilder; // Set the style according to the theme. @@ -88,11 +145,14 @@ public class CreateHomeScreenShortcutDialog extends DialogFragment { // Set the view. The parent view is null because it will be assigned by the alert dialog. dialogBuilder.setView(layoutInflater.inflate(R.layout.create_home_screen_shortcut_dialog, null)); - // Setup the negative button. Using null closes the dialog without doing anything else. + // Setup the close button. Using null closes the dialog without doing anything else. dialogBuilder.setNegativeButton(R.string.cancel, null); - // Set an `onClick` listener on the positive button. - dialogBuilder.setPositiveButton(R.string.create, (DialogInterface dialog, int which) -> createHomeScreenShortcutListener.onCreateHomeScreenShortcut(CreateHomeScreenShortcutDialog.this)); + // Set an `onClick` listener on the create button. + dialogBuilder.setPositiveButton(R.string.create, (DialogInterface dialog, int which) -> { + // Create the home screen shortcut. + createHomeScreenShortcut(); + }); // Create an alert dialog from the alert dialog builder. final AlertDialog alertDialog = dialogBuilder.create(); @@ -105,36 +165,146 @@ public class CreateHomeScreenShortcutDialog extends DialogFragment { alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } - // Show the keyboard when the Dialog is displayed on the screen. - alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - - // We need to show the alert dialog before we can call `setOnKeyListener()` below. + // The alert dialog must be shown before the contents may be edited. alertDialog.show(); - // Get a handle for `shortcut_name_edittext`. - EditText shortcutNameEditText = alertDialog.findViewById(R.id.shortcut_name_edittext); + // Get a handle for the edit texts. + shortcutNameEditText = alertDialog.findViewById(R.id.shortcut_name_edittext); + urlEditText = alertDialog.findViewById(R.id.url_edittext); + + // Populate the edit texts. + shortcutNameEditText.setText(initialShortcutName); + urlEditText.setText(initialUrlString); - // Set the current `WebView` title as the text for `shortcutNameEditText`. - shortcutNameEditText.setText(MainWebViewActivity.webViewTitle); + // Get a handle for the create button. + createButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - // Allow the "enter" key on the keyboard to create the shortcut. - shortcutNameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> { - // If the event is a key-down on the "enter" button, select the PositiveButton "Create". - if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { - // Trigger the create listener. - createHomeScreenShortcutListener.onCreateHomeScreenShortcut(CreateHomeScreenShortcutDialog.this); + // Add a text change listener to the shortcut name edit text. + shortcutNameEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing. + } - // Manually dismiss `alertDialog`. - alertDialog.dismiss(); + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing. + } + + @Override + public void afterTextChanged(Editable s) { + // Update the create button. + updateCreateButton(); + } + }); + + // Add a text change listener to the URL edit text. + urlEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing. + } - // Consume the event. - return true; - } else { // If any other key was pressed, do not consume the event. + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing. + } + + @Override + public void afterTextChanged(Editable s) { + // Update the create button. + updateCreateButton(); + } + }); + + // Allow the enter key on the keyboard to create the shortcut when editing the name. + shortcutNameEditText.setOnKeyListener((View view, int keyCode, KeyEvent keyEvent) -> { + // Check to see if the enter key was pressed. + if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + // Check the status of the create button. + if (createButton.isEnabled()) { // The create button is enabled. + // Create the home screen shortcut. + createHomeScreenShortcut(); + + // Manually dismiss the alert dialog. + alertDialog.dismiss(); + + // Consume the event. + return true; + } else { // The create button is disabled. + // Do not consume the event. + return false; + } + } else { // Some other key was pressed. + // Do not consume the event. + return false; + } + }); + + // Set the enter key on the keyboard to create the shortcut when editing the URL. + urlEditText.setOnKeyListener((View view, int keyCode, KeyEvent keyEvent) -> { + // Check to see if the enter key was pressed. + if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + // Check the status of the create button. + if (createButton.isEnabled()) { // The create button is enabled. + // Create the home screen shortcut. + createHomeScreenShortcut(); + + // Manually dismiss the alert dialog. + alertDialog.dismiss(); + + // Consume the event. + return true; + } else { // The create button is disabled. + // Do not consume the event. + return false; + } + } else { // Some other key was pressed. + // Do not consume the event. return false; } }); - // `onCreateDialog` requires the return of an `AlertDialog`. + // Return the alert dialog. return alertDialog; } + + private void updateCreateButton() { + // Get the contents of the edit texts. + String shortcutName = shortcutNameEditText.getText().toString(); + String urlString = urlEditText.getText().toString(); + + // Enable the create button if both the shortcut name and the URL are not empty. + createButton.setEnabled(!shortcutName.isEmpty() && !urlString.isEmpty()); + } + + private void createHomeScreenShortcut() { + // Get a handle for the context. + Context context = getContext(); + + // Remove the incorrect lint warning below that the context might be null. + assert context != null; + + // Get the strings from the edit texts. + String shortcutName = shortcutNameEditText.getText().toString(); + String urlString = urlEditText.getText().toString(); + + // Convert the favorite icon bitmap to an icon. `IconCompat` must be used until the minimum API >= 26. + IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap); + + // Setup the shortcut intent. + Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); + shortcutIntent.setData(Uri.parse(urlString)); + + // Create a shortcut info builder. The shortcut name becomes the shortcut ID. + ShortcutInfoCompat.Builder shortcutInfoBuilder = new ShortcutInfoCompat.Builder(context, shortcutName); + + // Add the required fields to the shortcut info builder. + shortcutInfoBuilder.setIcon(favoriteIcon); + shortcutInfoBuilder.setIntent(shortcutIntent); + shortcutInfoBuilder.setShortLabel(shortcutName); + + // Add the shortcut to the home screen. `ShortcutManagerCompat` can be switched to `ShortcutManager` once the minimum API >= 26. + ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoBuilder.build(), null); + } } \ No newline at end of file diff --git a/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml b/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml index 5931361b..3b3c2449 100644 --- a/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml +++ b/app/src/main/res/layout/create_home_screen_shortcut_dialog.xml @@ -33,12 +33,11 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_marginTop="16dp" - android:layout_marginBottom="10dp" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" > + android:layout_marginBottom="5dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" > - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 63337104..3ddc1242 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -502,6 +502,8 @@ Pantalla completa Navegación de pantalla completa Doble toque para alternar a modo de navegación de pantalla completa. + Ocultar la barra de aplicaciones + Ocultar la barra de aplicaciones que contiene la URL. Borrar todo Borra cookies, almacenamiento DOM, datos de formulario y la caché de WebView. A continuación borra manualmente los directorios “app_webview” y “cache”. @@ -539,6 +541,8 @@ Deslizar para actualizar Algunas webs no funcionan bien si la opción deslizar para actualizar está habilitada. + Desplazar la barra de aplicaciones + Desplazar la barra de aplicaciones desde la parte superior de la pantalla cuando el WebView se desplaza hacia abajo. Mostrar iconos adicionales en la barra de aplicación Mostrar iconos en la barra de aplicaciones para refrescar el WebView y, si hay espacio, para alternar entre cookies y almacenamiento DOM. Descargar con app externa diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d9898476..5d293688 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -495,6 +495,8 @@ Во весь экран Полноэкранный режим просмотра Двойное касание переключает режим просмотра. + Скрыть панель приложения + Скрывает панель приложения, которая содержит URL. Очистить все Очищает файлы cookie, DOM-хранилище, данные формы и кэш WebView. Затем вручную удаляются все каталоги "app_webview" и "cache". Очистить файлы cookie @@ -531,6 +533,8 @@ Потянуть для обновления Некоторые веб-сайты могут работать некорректно при включении данной опции. + Прокручивать панель приложения + Прокручивает панель приложения вверху экрана при прокрутке WebView вниз. Отображать дополнительные значки на панели приложения Отображать значки на панели приложения для обновления WebView и, при наличии места, для переключения файлов cookie и хранилища DOM Загрузка с помощью внешнего приложения -- 2.43.0