mirror of
https://github.com/TrianguloY/UrlChecker.git
synced 2024-09-19 20:02:16 +02:00
Use the component (package+class) instead of the package only for intent-related operations.
Shows the icon + real label too Hopefully fixes #196
This commit is contained in:
parent
49a7bd164b
commit
143ddfb375
@ -1,9 +1,11 @@
|
||||
package com.trianguloy.urlchecker.modules.companions;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
||||
import com.trianguloy.urlchecker.utilities.generics.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.methods.JavaUtils;
|
||||
import com.trianguloy.urlchecker.utilities.wrappers.IntentApp;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
@ -44,27 +46,28 @@ public class LastOpened {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an existing list of [packages] with the preferred order
|
||||
* Sorts an existing list of [intentApps] with the preferred order
|
||||
*/
|
||||
public void sort(List<String> packages, String url) {
|
||||
Collections.sort(packages, (from, another) -> comparePrefer(from, another, url));
|
||||
public void sort(List<IntentApp> intentApps, String url) {
|
||||
Collections.sort(intentApps, (from, another) ->
|
||||
comparePrefer(from.getComponent(), another.getComponent(), url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the [prefer] package as preferred over [others].
|
||||
* Marks the [prefer] intentApp as preferred over [others].
|
||||
*/
|
||||
public void prefer(String prefer, List<String> others, String url) {
|
||||
for (String other : others) {
|
||||
prefer(prefer, other, 1, url);
|
||||
public void prefer(IntentApp prefer, List<IntentApp> others, String url) {
|
||||
for (var other : others) {
|
||||
prefer(prefer.getComponent(), other.getComponent(), 1, url);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------- private ------------------- */
|
||||
|
||||
/**
|
||||
* Marks that [prefer] package is preferred over [other] as much as [amount] more
|
||||
* Marks that [prefer] component is preferred over [other] as much as [amount] more
|
||||
*/
|
||||
private void prefer(String prefer, String other, int amount, String url) {
|
||||
private void prefer(ComponentName prefer, ComponentName other, int amount, String url) {
|
||||
// skip prefer over ourselves, it's useless
|
||||
if (prefer.equals(other)) return;
|
||||
|
||||
@ -80,10 +83,10 @@ public class LastOpened {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current preference between these two packages.
|
||||
* Returns the current preference between these two components.
|
||||
* Equivalent result as [from].compareTo([another])
|
||||
*/
|
||||
private int comparePrefer(String from, String another, String url) {
|
||||
private int comparePrefer(ComponentName from, ComponentName another, String url) {
|
||||
// switch order if not lexicographically sorted
|
||||
if (from.compareTo(another) > 0) {
|
||||
return -comparePrefer(another, from, url);
|
||||
@ -94,10 +97,10 @@ public class LastOpened {
|
||||
}
|
||||
|
||||
/**
|
||||
* The preference between two packages. ([left] must be lexicographically less than [right])
|
||||
* The preference between two components. ([left] must be lexicographically less than [right])
|
||||
*/
|
||||
private GenericPref.Int getPref(String left, String right, String url) {
|
||||
String prefName = String.format(PREFIX, left, right);
|
||||
private GenericPref.Int getPref(ComponentName left, ComponentName right, String url) {
|
||||
String prefName = String.format(PREFIX, left.flattenToShortString(), right.flattenToShortString());
|
||||
if (perDomainPref.get()) {
|
||||
prefName = getDomain(url) + " " + prefName;
|
||||
}
|
||||
|
@ -22,11 +22,14 @@ import com.trianguloy.urlchecker.modules.companions.LastOpened;
|
||||
import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.generics.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.methods.AndroidUtils;
|
||||
import com.trianguloy.urlchecker.utilities.methods.JavaUtils;
|
||||
import com.trianguloy.urlchecker.utilities.methods.PackageUtils;
|
||||
import com.trianguloy.urlchecker.utilities.methods.UrlUtils;
|
||||
import com.trianguloy.urlchecker.utilities.wrappers.IntentApp;
|
||||
import com.trianguloy.urlchecker.utilities.wrappers.RejectionDetector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This module contains an open and share buttons
|
||||
@ -92,7 +95,7 @@ class OpenDialog extends AModuleDialog {
|
||||
private final Incognito incognito;
|
||||
private final RejectionDetector rejectionDetector;
|
||||
|
||||
private List<String> packages;
|
||||
private List<IntentApp> intentApps;
|
||||
private Button btn_open;
|
||||
private ImageButton btn_openWith;
|
||||
private View openParent;
|
||||
@ -171,26 +174,27 @@ class OpenDialog extends AModuleDialog {
|
||||
|
||||
// ------------------- Spinner -------------------
|
||||
|
||||
/**
|
||||
* Populates the spinner with the apps that can open it, in preference order
|
||||
*/
|
||||
/** Populates the spinner with the apps that can open it, in preference order */
|
||||
private void updateSpinner(String url) {
|
||||
packages = PackageUtils.getOtherPackages(UrlUtils.getViewIntent(url, null), getActivity());
|
||||
intentApps = IntentApp.getOtherPackages(UrlUtils.getViewIntent(url, null), getActivity());
|
||||
|
||||
// remove referrer
|
||||
if (noReferrerPref.get()) {
|
||||
packages.remove(AndroidUtils.getReferrer(getActivity()));
|
||||
var referrer = AndroidUtils.getReferrer(getActivity());
|
||||
JavaUtils.removeIf(intentApps, ri -> Objects.equals(ri.getPackage(), referrer));
|
||||
}
|
||||
|
||||
// remove rejected if desired (and is not a non-view action, like share)
|
||||
// note: this will be called each time, so a rejected package will not be rejected again if the user changes the url and goes back. This is expected
|
||||
if (rejectedPref.get() && Intent.ACTION_VIEW.equals(getActivity().getIntent().getAction())) {
|
||||
packages.remove(rejectionDetector.getPrevious(url));
|
||||
var rejected = rejectionDetector.getPrevious(url);
|
||||
JavaUtils.removeIf(intentApps, ri -> Objects.equals(ri.getComponent(), rejected));
|
||||
}
|
||||
|
||||
// check no apps
|
||||
if (packages.isEmpty()) {
|
||||
if (intentApps.isEmpty()) {
|
||||
btn_open.setText(R.string.mOpen_noapps);
|
||||
btn_open.setCompoundDrawables(null, null, null, null);
|
||||
AndroidUtils.setEnabled(openParent, false);
|
||||
btn_open.setEnabled(false);
|
||||
btn_openWith.setVisibility(View.GONE);
|
||||
@ -198,19 +202,24 @@ class OpenDialog extends AModuleDialog {
|
||||
}
|
||||
|
||||
// sort
|
||||
lastOpened.sort(packages, getUrl());
|
||||
lastOpened.sort(intentApps, getUrl());
|
||||
|
||||
// set
|
||||
btn_open.setText(getActivity().getString(R.string.mOpen_with, PackageUtils.getPackageName(packages.get(0), getActivity())));
|
||||
var label = intentApps.get(0).getLabel(getActivity());
|
||||
// label = getActivity().getString(R.string.mOpen_with, label);
|
||||
btn_open.setText(label);
|
||||
btn_open.setCompoundDrawables(intentApps.get(0).getIcon(getActivity()), null, null, null);
|
||||
AndroidUtils.setEnabled(openParent, true);
|
||||
btn_open.setEnabled(true);
|
||||
menu.clear();
|
||||
if (packages.size() == 1) {
|
||||
if (intentApps.size() == 1) {
|
||||
btn_openWith.setVisibility(View.GONE);
|
||||
} else {
|
||||
btn_openWith.setVisibility(View.VISIBLE);
|
||||
for (int i = 1; i < packages.size(); i++) {
|
||||
menu.add(Menu.NONE, i, i, getActivity().getString(R.string.mOpen_with, PackageUtils.getPackageName(packages.get(i), getActivity())));
|
||||
for (int i = 1; i < intentApps.size(); i++) {
|
||||
label = intentApps.get(i).getLabel(getActivity());
|
||||
// label = getActivity().getString(R.string.mOpen_with, label);
|
||||
menu.add(Menu.NONE, i, i, label);//.setIcon(intentApps.get(i).getIcon(getActivity()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,23 +230,22 @@ class OpenDialog extends AModuleDialog {
|
||||
/**
|
||||
* Open url in a specific app
|
||||
*
|
||||
* @param index index from the packages list of the app to use
|
||||
* @param index index from the intentApps list of the app to use
|
||||
*/
|
||||
private void openUrl(int index) {
|
||||
// get
|
||||
if (index < 0 || index >= packages.size()) return;
|
||||
var chosen = packages.get(index);
|
||||
if (index < 0 || index >= intentApps.size()) return;
|
||||
var chosen = intentApps.get(index);
|
||||
|
||||
// update as preferred over the rest
|
||||
lastOpened.prefer(chosen, packages, getUrl());
|
||||
lastOpened.prefer(chosen, intentApps, getUrl());
|
||||
|
||||
// open
|
||||
var intent = new Intent(getActivity().getIntent());
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||
// preserve original VIEW intent
|
||||
intent.setData(Uri.parse(getUrl()));
|
||||
intent.setComponent(null);
|
||||
intent.setPackage(chosen);
|
||||
intent.setComponent(chosen.getComponent());
|
||||
} else {
|
||||
// replace with new VIEW intent
|
||||
intent = UrlUtils.getViewIntent(getUrl(), chosen);
|
||||
|
@ -85,16 +85,10 @@ public interface JavaUtils {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as Arrays.compare, which is not available in api < 33
|
||||
*/
|
||||
static int compareArrays(int[] l, int[] r) {
|
||||
int i = 0;
|
||||
while (i < l.length && i < r.length) {
|
||||
if (l[i] != r[i]) return Integer.signum(r[i] - l[i]);
|
||||
i++;
|
||||
}
|
||||
return Integer.signum(r.length - l.length);
|
||||
/** Removes elements from a collection matching a predicate */
|
||||
static <E> void removeIf(Collection<E> collection, Function<E, Boolean> predicate) {
|
||||
var iterator = collection.iterator();
|
||||
while (iterator.hasNext()) if (predicate.apply(iterator.next())) iterator.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,62 +3,13 @@ package com.trianguloy.urlchecker.utilities.methods;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Static utilities related to packages
|
||||
*/
|
||||
public interface PackageUtils {
|
||||
|
||||
/**
|
||||
* Returns a list of packages that can open an intent, removing this app from the list
|
||||
*
|
||||
* @param baseIntent intent that the packages will be able to open
|
||||
* @param cntx base context (and the app that will be filtered)
|
||||
* @return the list of other packages
|
||||
*/
|
||||
static List<String> getOtherPackages(Intent baseIntent, Context cntx) {
|
||||
List<String> packages = new ArrayList<>();
|
||||
|
||||
// get all packages
|
||||
PackageManager packageManager = cntx.getPackageManager();
|
||||
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(baseIntent, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PackageManager.MATCH_ALL : 0);
|
||||
|
||||
// filter the current app
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
if (!resolveInfo.activityInfo.packageName.equals(cntx.getPackageName())) {
|
||||
packages.add(resolveInfo.activityInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app name of a package
|
||||
*
|
||||
* @param pack packagename to search
|
||||
* @param cntx base context
|
||||
* @return that app name
|
||||
*/
|
||||
static String getPackageName(String pack, Context cntx) {
|
||||
final PackageManager pm = cntx.getPackageManager();
|
||||
try {
|
||||
// try getting the app label
|
||||
return pm.getApplicationLabel(pm.getApplicationInfo(pack, PackageManager.GET_META_DATA)).toString();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// can't get the label
|
||||
e.printStackTrace();
|
||||
return cntx.getString(android.R.string.unknownName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link Context#startActivity(Intent)} to catch thrown exceptions and show a toast instead
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@ import android.os.Parcelable;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.trianguloy.urlchecker.R;
|
||||
import com.trianguloy.urlchecker.utilities.wrappers.IntentApp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -19,13 +20,13 @@ public interface UrlUtils {
|
||||
/**
|
||||
* Returns an intent that will open the given url, with an optional package
|
||||
*
|
||||
* @param url the url that will be opened
|
||||
* @param packageName the package that will be opened, null to let android choose
|
||||
* @param url the url that will be opened
|
||||
* @param intentApp the intentApp that will be opened, null to let android choose
|
||||
* @return the converted intent
|
||||
*/
|
||||
static Intent getViewIntent(String url, String packageName) {
|
||||
static Intent getViewIntent(String url, IntentApp intentApp) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
if (packageName != null) intent.setPackage(packageName);
|
||||
if (intentApp != null) intent.setComponent(intentApp.getComponent());
|
||||
return intent;
|
||||
}
|
||||
|
||||
@ -37,9 +38,9 @@ public interface UrlUtils {
|
||||
*/
|
||||
static void openUrlRemoveThis(String url, Context cntx) {
|
||||
|
||||
// get packages that can open the url
|
||||
// get intents that can open the url
|
||||
List<Intent> intents = new ArrayList<>();
|
||||
for (String pack : PackageUtils.getOtherPackages(getViewIntent(url, null), cntx)) {
|
||||
for (var pack : IntentApp.getOtherPackages(getViewIntent(url, null), cntx)) {
|
||||
intents.add(getViewIntent(url, pack));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,78 @@
|
||||
package com.trianguloy.urlchecker.utilities.wrappers;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import com.trianguloy.urlchecker.dialogs.MainDialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Represents a way to open something (A wrapper of {@link ResolveInfo}) */
|
||||
public class IntentApp {
|
||||
|
||||
/**
|
||||
* Returns a list of packages that can open an intent, removing this app from the list
|
||||
*
|
||||
* @param baseIntent intent that the packages will be able to open
|
||||
* @param cntx base context (and the app that will be filtered)
|
||||
* @return the list of other packages
|
||||
*/
|
||||
public static List<IntentApp> getOtherPackages(Intent baseIntent, Context cntx) {
|
||||
// get all packages
|
||||
var resolveInfos = cntx.getPackageManager().queryIntentActivityOptions(
|
||||
new ComponentName(cntx, MainDialog.class.getName()),
|
||||
null,
|
||||
baseIntent,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PackageManager.MATCH_ALL : 0);
|
||||
|
||||
var intentApps = new ArrayList<IntentApp>();
|
||||
for (var resolveInfo : resolveInfos) intentApps.add(new IntentApp(resolveInfo));
|
||||
return intentApps;
|
||||
}
|
||||
|
||||
/* ------------------- wrapper ------------------- */
|
||||
|
||||
private final ResolveInfo resolveInfo;
|
||||
private static final Map<ComponentName, CharSequence> labelsCache = new HashMap<>();
|
||||
private static final Map<ComponentName, Drawable> iconsCache = new HashMap<>();
|
||||
|
||||
private IntentApp(ResolveInfo resolveInfo) {
|
||||
this.resolveInfo = resolveInfo;
|
||||
}
|
||||
|
||||
/** Returns the package only */
|
||||
public String getPackage() {
|
||||
return resolveInfo.activityInfo.packageName;
|
||||
}
|
||||
|
||||
/** Returns the component (package + class) */
|
||||
public ComponentName getComponent() {
|
||||
return new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
|
||||
}
|
||||
|
||||
/** Returns the label, cached */
|
||||
public CharSequence getLabel(Context activity) {
|
||||
var component = getComponent();
|
||||
if (!labelsCache.containsKey(component)) labelsCache.put(component, resolveInfo.loadLabel(activity.getPackageManager()));
|
||||
return labelsCache.get(component);
|
||||
}
|
||||
|
||||
/** Returns the drawable, cached */
|
||||
public Drawable getIcon(Context activity) {
|
||||
var component = getComponent();
|
||||
if (!iconsCache.containsKey(component)) {
|
||||
var icon = resolveInfo.loadIcon(activity.getPackageManager());
|
||||
icon.setBounds(0, 0, 50, 50);
|
||||
iconsCache.put(component, icon);
|
||||
}
|
||||
return iconsCache.get(component);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.trianguloy.urlchecker.utilities.wrappers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
|
||||
import com.trianguloy.urlchecker.utilities.generics.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.methods.AndroidUtils;
|
||||
@ -16,7 +17,7 @@ import java.util.Objects;
|
||||
public class RejectionDetector {
|
||||
|
||||
private static final int TIMEFRAME = 5000;
|
||||
private final GenericPref.LstStr rejectLast; // [openedTimeMillis, package, url]
|
||||
private final GenericPref.LstStr rejectLast; // [openedTimeMillis, component, url]
|
||||
private final Activity cntx;
|
||||
|
||||
public RejectionDetector(Activity cntx) {
|
||||
@ -24,21 +25,19 @@ public class RejectionDetector {
|
||||
this.cntx = cntx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a url as opened to a package (at this moment)
|
||||
*/
|
||||
public void markAsOpen(String url, String packageName) {
|
||||
rejectLast.set(List.of(Long.toString(System.currentTimeMillis()), packageName, url));
|
||||
/** Marks a url as opened from an intentApp (at this moment) */
|
||||
public void markAsOpen(String url, IntentApp intentApp) {
|
||||
rejectLast.set(List.of(Long.toString(System.currentTimeMillis()), intentApp.getComponent().toShortString(), url));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the last package that opened the url if
|
||||
* returns the last component that opened the url if
|
||||
* - it happened in a short amount of time
|
||||
* - (and) the url is the same
|
||||
* - (and) the referrer app is the same
|
||||
* null otherwise
|
||||
*/
|
||||
public String getPrevious(String url) {
|
||||
public ComponentName getPrevious(String url) {
|
||||
|
||||
try {
|
||||
var data = rejectLast.get();
|
||||
@ -49,7 +48,7 @@ public class RejectionDetector {
|
||||
&& Objects.equals(data.get(2), url)
|
||||
&& Objects.equals(AndroidUtils.getReferrer(cntx), data.get(1))
|
||||
|
||||
? data.get(1)
|
||||
? ComponentName.unflattenFromString(data.get(1))
|
||||
: null;
|
||||
} catch (Exception ignore) {
|
||||
// just ignore errors while retrieving the data
|
||||
|
Loading…
Reference in New Issue
Block a user