From d494c31e3c8cebdedae540fa7ca77f18fa6bf860 Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 15 Jun 2022 15:36:29 +0200 Subject: [PATCH] Select browser on opening link --- app/src/amazon/AndroidManifest.xml | 5 + app/src/fdroid/AndroidManifest.xml | 5 + app/src/github/AndroidManifest.xml | 5 + app/src/main/AndroidManifest.xml | 5 + .../eu/faircode/email/AdapterMessage.java | 4 +- .../java/eu/faircode/email/ApplicationEx.java | 3 + .../email/FragmentDialogOpenLink.java | 129 +++++++++++++++--- .../eu/faircode/email/FragmentMessages.java | 15 ++ .../email/FragmentOptionsPrivacy.java | 12 +- .../main/java/eu/faircode/email/Helper.java | 20 ++- app/src/main/res/layout/dialog_open_link.xml | 31 ++++- .../res/layout/fragment_options_privacy.xml | 14 +- app/src/main/res/menu/menu_messages.xml | 7 + app/src/main/res/values/strings.xml | 3 +- app/src/play/AndroidManifest.xml | 5 + 15 files changed, 209 insertions(+), 54 deletions(-) diff --git a/app/src/amazon/AndroidManifest.xml b/app/src/amazon/AndroidManifest.xml index 20a369edb2..c987d1f0da 100644 --- a/app/src/amazon/AndroidManifest.xml +++ b/app/src/amazon/AndroidManifest.xml @@ -52,6 +52,11 @@ + + + + + diff --git a/app/src/fdroid/AndroidManifest.xml b/app/src/fdroid/AndroidManifest.xml index aabac4262b..ee3f695cf7 100644 --- a/app/src/fdroid/AndroidManifest.xml +++ b/app/src/fdroid/AndroidManifest.xml @@ -52,6 +52,11 @@ + + + + + diff --git a/app/src/github/AndroidManifest.xml b/app/src/github/AndroidManifest.xml index 0fa9fd3659..afc490d16b 100644 --- a/app/src/github/AndroidManifest.xml +++ b/app/src/github/AndroidManifest.xml @@ -52,6 +52,11 @@ + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 426d2484b6..02973f2a1b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,6 +52,11 @@ + + + + + diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 5fd5a8208b..83b8896734 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -5673,8 +5673,8 @@ public class AdapterMessage extends RecyclerView.Adapter= Build.VERSION_CODES.O && !BuildConfig.DEBUG) diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java b/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java index e59c11e1b9..766604afec 100644 --- a/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java +++ b/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java @@ -19,14 +19,17 @@ package eu.faircode.email; Copyright 2018-2022 by Marcel Bokhorst (M66B) */ +import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION; + import android.app.Dialog; -import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.Paint; import android.graphics.Typeface; import android.net.Uri; @@ -46,11 +49,14 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageButton; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -66,7 +72,9 @@ import androidx.preference.PreferenceManager; import java.net.IDN; import java.net.InetAddress; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -104,6 +112,46 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { final Uri uri = UriHelper.guessScheme(_uri); + String def = null; + List pkgs = new ArrayList<>(); + if (UriHelper.isHyperLink(uri)) { + try { + PackageManager pm = context.getPackageManager(); + Intent intent = new Intent(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(uri); + + ResolveInfo main = pm.resolveActivity(intent, 0); + if (main != null) + def = main.activityInfo.packageName; + + int flags = (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PackageManager.MATCH_ALL); + List ris = pm.queryIntentActivities(intent, flags); + for (ResolveInfo ri : ris) { + CharSequence label = pm.getApplicationLabel(ri.activityInfo.applicationInfo); + if (label == null) + continue; + pkgs.add(new Package(label.toString(), ri.activityInfo.packageName, false)); + + try { + Intent serviceIntent = new Intent(); + serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION); + serviceIntent.setPackage(ri.activityInfo.packageName); + boolean tabs = (pm.resolveService(serviceIntent, 0) != null); + if (tabs) + pkgs.add(new Package( + getString(R.string.title_browse_embedded, label), + ri.activityInfo.packageName, + tabs)); + } catch (Throwable ex) { + Log.e(ex); + } + } + } catch (Throwable ex) { + Log.e(ex); + } + } + // Process link final Uri sanitized; if (uri.isOpaque()) @@ -139,6 +187,7 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { final CheckBox cbSecure = dview.findViewById(R.id.cbSecure); final CheckBox cbSanitize = dview.findViewById(R.id.cbSanitize); final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); + final Spinner spOpenWith = dview.findViewById(R.id.spOpenWith); ibMore = dview.findViewById(R.id.ibMore); tvMore = dview.findViewById(R.id.tvMore); @@ -152,6 +201,7 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { btnDefault = dview.findViewById(R.id.btnDefault); tvReset = dview.findViewById(R.id.tvReset); + final Group grpOpenWith = dview.findViewById(R.id.grpOpenWith); final Group grpDifferent = dview.findViewById(R.id.grpDifferent); // Wire @@ -298,6 +348,44 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { } }); + ArrayAdapter adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, android.R.id.text1); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + adapter.addAll(pkgs); + spOpenWith.setAdapter(adapter); + + String open_with_pkg = prefs.getString("open_with_pkg", null); + boolean open_with_tabs = prefs.getBoolean("open_with_tabs", true); + for (int position = 0; position < pkgs.size(); position++) { + Package pkg = pkgs.get(position); + if (Objects.equals(pkg.name, open_with_pkg) && pkg.tabs == open_with_tabs) { + spOpenWith.setSelection(position); + break; + } + if (Objects.equals(def, pkg.name)) + spOpenWith.setSelection(position); + } + + spOpenWith.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + Package pkg = pkgs.get(position); + prefs.edit() + .putString("open_with_pkg", pkg.name) + .putBoolean("open_with_tabs", pkg.tabs) + .apply(); + } + + @Override + public void onNothingSelected(AdapterView parent) { + prefs.edit() + .remove("open_with_pkg") + .remove("open_with_tabs") + .apply(); + } + }); + + grpOpenWith.setVisibility(pkgs.size() > 0 ? View.VISIBLE : View.GONE); + View.OnClickListener onMore = new View.OnClickListener() { @Override public void onClick(View v) { @@ -469,24 +557,10 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { @Override public void onClick(DialogInterface dialog, int which) { Uri uri = Uri.parse(etLink.getText().toString()); - Log.i("Open link uri=" + uri); - Helper.view(context, uri, false); - } - }) - .setNeutralButton(R.string.title_browse, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // https://developer.android.com/training/basics/intents/sending#AppChooser - Uri uri = Uri.parse(etLink.getText().toString()); - Log.i("Open link with uri=" + uri); - Intent view = new Intent(Intent.ACTION_VIEW, uri); - Intent chooser = Intent.createChooser(view, context.getString(R.string.title_select_app)); - try { - startActivity(chooser); - } catch (ActivityNotFoundException ex) { - Log.w(ex); - Helper.view(context, uri, true, true); - } + Package pkg = (Package) spOpenWith.getSelectedItem(); + Log.i("Open link uri=" + uri + " with=" + pkg); + boolean tabs = (pkg != null && pkg.tabs); + Helper.view(context, uri, !tabs, !tabs); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @@ -567,4 +641,21 @@ public class FragmentDialogOpenLink extends FragmentDialogBase { return ssb; } + + private static class Package { + String title; + String name; + boolean tabs; + + public Package(String title, String name, boolean tabs) { + this.title = title; + this.name = name; + this.tabs = tabs; + } + + @Override + public String toString() { + return title; + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index ce385af387..05a6345694 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -4985,6 +4985,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. String filter_language = prefs.getString("filter_language", null); boolean perform_expunge = prefs.getBoolean("perform_expunge", true); boolean compact = prefs.getBoolean("compact", false); + boolean confirm_links = prefs.getBoolean("confirm_links", true); int zoom = prefs.getInt("view_zoom", compact ? 0 : 1); int padding = prefs.getInt("view_padding", compact || !cards ? 0 : 1); boolean quick_filter = prefs.getBoolean("quick_filter", false); @@ -5100,6 +5101,10 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. menu.findItem(R.id.menu_theme).setVisible(viewType == AdapterMessage.ViewType.UNIFIED); + menu.findItem(R.id.menu_confirm_links) + .setChecked(confirm_links) + .setVisible(viewType == AdapterMessage.ViewType.THREAD); + menu.findItem(R.id.menu_select_all).setVisible(folder); menu.findItem(R.id.menu_select_found).setVisible(viewType == AdapterMessage.ViewType.SEARCH); menu.findItem(R.id.menu_mark_all_read).setVisible(folder); @@ -5241,6 +5246,9 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. } else if (itemId == R.id.menu_theme) { onMenuTheme(); return true; + } else if (itemId == R.id.menu_confirm_links) { + onMenuConfirmLinks(); + return true; } else if (itemId == R.id.menu_select_all || itemId == R.id.menu_select_found) { onMenuSelectAll(); return true; @@ -5570,6 +5578,13 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. new FragmentDialogTheme().show(getParentFragmentManager(), "messages:theme"); } + private void onMenuConfirmLinks() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + boolean compact = !prefs.getBoolean("confirm_links", true); + prefs.edit().putBoolean("confirm_links", compact).apply(); + invalidateOptionsMenu(); + } + private void clearMeasurements() { sizes.clear(); heights.clear(); diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java index 7f40293042..11f70f87a3 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java @@ -66,7 +66,6 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer private ImageButton ibHelp; private SwitchCompat swConfirmLinks; private SwitchCompat swCheckLinksDbl; - private SwitchCompat swBrowseLinks; private SwitchCompat swConfirmImages; private SwitchCompat swAskImages; private SwitchCompat swHtmlImages; @@ -107,7 +106,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer private final static int BIP39_WORDS = 6; private final static String[] RESET_OPTIONS = new String[]{ - "confirm_links", "check_links_dbl", "browse_links", + "confirm_links", "check_links_dbl", "confirm_images", "ask_images", "html_always_images", "confirm_html", "ask_html", "disable_tracking", "pin", "biometrics", "biometrics_timeout", "autolock", "autolock_nav", @@ -131,7 +130,6 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer ibHelp = view.findViewById(R.id.ibHelp); swConfirmLinks = view.findViewById(R.id.swConfirmLinks); swCheckLinksDbl = view.findViewById(R.id.swCheckLinksDbl); - swBrowseLinks = view.findViewById(R.id.swBrowseLinks); swConfirmImages = view.findViewById(R.id.swConfirmImages); swAskImages = view.findViewById(R.id.swAskImages); swHtmlImages = view.findViewById(R.id.swHtmlImages); @@ -197,13 +195,6 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer } }); - swBrowseLinks.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - prefs.edit().putBoolean("browse_links", checked).apply(); - } - }); - swConfirmImages.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @@ -546,7 +537,6 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer swConfirmLinks.setChecked(prefs.getBoolean("confirm_links", true)); swCheckLinksDbl.setChecked(prefs.getBoolean("check_links_dbl", BuildConfig.PLAY_STORE_RELEASE)); swCheckLinksDbl.setEnabled(swConfirmLinks.isChecked()); - swBrowseLinks.setChecked(prefs.getBoolean("browse_links", false)); swConfirmImages.setChecked(prefs.getBoolean("confirm_images", true)); swAskImages.setChecked(prefs.getBoolean("ask_images", true)); swAskImages.setEnabled(swConfirmImages.isChecked()); diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 31519ef2c2..1980b18e41 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -410,7 +410,7 @@ public class Helper { return permissions.toArray(new String[0]); } - static boolean hasCustomTabs(Context context, Uri uri) { + private static boolean hasCustomTabs(Context context, Uri uri, String pkg) { String scheme = (uri == null ? null : uri.getScheme()); if (!"http".equals(scheme) && !"https".equals(scheme)) return false; @@ -423,6 +423,8 @@ public class Helper { Intent intent = new Intent(); intent.setAction(ACTION_CUSTOM_TABS_CONNECTION); intent.setPackage(info.activityInfo.packageName); + if (pkg != null && !pkg.equals(info.activityInfo.packageName)) + continue; if (pm.resolveService(intent, 0) != null) return true; } @@ -821,8 +823,12 @@ public class Helper { return; } - boolean has = hasCustomTabs(context, uri); - Log.i("View=" + uri + " browse=" + browse + " task=" + task + " has=" + has); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String open_with_pkg = prefs.getString("open_with_pkg", null); + + boolean has = hasCustomTabs(context, uri, open_with_pkg); + + Log.i("View=" + uri + " browse=" + browse + " task=" + task + " pkg=" + open_with_pkg + " has=" + has); if (browse || !has) { try { @@ -833,12 +839,14 @@ public class Helper { view.setDataAndType(uri, mimeType); if (task) view.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (UriHelper.isHyperLink(uri) && + open_with_pkg != null && isInstalled(context, open_with_pkg)) + view.setPackage(open_with_pkg); context.startActivity(view); } catch (Throwable ex) { reportNoViewer(context, uri, ex); } } else { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean navbar_colorize = prefs.getBoolean("navbar_colorize", false); int colorPrimary = resolveColor(context, R.attr.colorPrimary); int colorPrimaryDark = resolveColor(context, R.attr.colorPrimaryDark); @@ -881,6 +889,10 @@ public class Helper { CustomTabsIntent customTabsIntent = builder.build(); customTabsIntent.intent.putExtra(Browser.EXTRA_HEADERS, headers); + + if (open_with_pkg != null && isInstalled(context, open_with_pkg)) + customTabsIntent.intent.setPackage(open_with_pkg); + try { customTabsIntent.launchUrl(context, uri); } catch (Throwable ex) { diff --git a/app/src/main/res/layout/dialog_open_link.xml b/app/src/main/res/layout/dialog_open_link.xml index 503aa7b0b3..de2187e594 100644 --- a/app/src/main/res/layout/dialog_open_link.xml +++ b/app/src/main/res/layout/dialog_open_link.xml @@ -75,8 +75,6 @@ android:id="@+id/tvLink" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="3dp" - android:layout_marginEnd="12dp" android:ellipsize="end" android:singleLine="true" android:text="Original link" @@ -197,6 +195,27 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbSanitize" /> + + + + + + - - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e215bb4d6..a2dfca488f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -653,7 +653,6 @@ Confirm opening links Check domain block lists for suspicious links - Delegate opening links to Android Show no images by default Confirm showing images Show reformatted messages by default @@ -1253,7 +1252,6 @@ Send hard bounce Reply with template Moving to %1$s (%2$d) - Open with %1$s authentication failed On spam block list @@ -1873,6 +1871,7 @@ Insert arrow Add Open with + %1$s (embedded) Info Download Report diff --git a/app/src/play/AndroidManifest.xml b/app/src/play/AndroidManifest.xml index ee4969ac6b..26cab85f8b 100644 --- a/app/src/play/AndroidManifest.xml +++ b/app/src/play/AndroidManifest.xml @@ -52,6 +52,11 @@ + + + + +