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;
// 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.
// `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;
// `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()`.
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:
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) {
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`.
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.
// 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();
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