From a6e38b7af265bb20687c9b946220e17210635c4a Mon Sep 17 00:00:00 2001 From: 4nric Date: Tue, 3 Dec 2024 22:37:00 +0800 Subject: [PATCH] Speed up app launches by prioritizing drawer apps. If not found, find from list of all apps. --- .../genymobile/scrcpy/control/Controller.java | 8 +- .../com/genymobile/scrcpy/device/Device.java | 181 +++++++++++++----- 2 files changed, 137 insertions(+), 52 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index 26db0b789e..5bea1776c7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -18,7 +18,6 @@ import android.content.ComponentName; import android.content.IOnPrimaryClipChangedListener; import android.content.Intent; -import android.content.pm.ResolveInfo; import android.os.Build; import android.os.SystemClock; import android.view.InputDevice; @@ -27,7 +26,6 @@ import android.view.MotionEvent; import java.io.IOException; -import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -639,19 +637,17 @@ private void startApp(String name) { Ln.i("Starting activity: " + name); } else { - List drawerApps = Device.getDrawerApps(); - boolean searchByName = name.startsWith("?") || name.contains(" "); if (searchByName) { if (name.contains("?")) { name = name.substring(1); } - launchIntent = Device.getAppWithUniqueLabel(drawerApps,name); + launchIntent = Device.getIntentFromAppDrawer(name,false); if (launchIntent == null){ return; } } else { - launchIntent = Device.getAppGivenPackageName(drawerApps,name); + launchIntent = Device.getIntentFromAppDrawer(name,true); if (launchIntent == null) { return; } diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Device.java b/server/src/main/java/com/genymobile/scrcpy/device/Device.java index a74c0ee0c1..79e23d0ccb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/device/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/device/Device.java @@ -18,6 +18,8 @@ import android.content.Context; import android.content.Intent; import android.app.ActivityOptions; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; @@ -244,8 +246,7 @@ public static void startApp(Intent launchIntent, int displayId, boolean forceSto @SuppressLint("QueryPermissionsNeeded") public static List getDrawerApps() { Context context = FakeContext.get(); - PackageManager packageManager = context.getPackageManager(); - + PackageManager pm = context.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN, null); UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE); @@ -254,66 +255,154 @@ public static List getDrawerApps() { } else { intent.addCategory(Intent.CATEGORY_LAUNCHER); } - return packageManager.queryIntentActivities(intent, 0); + return pm.queryIntentActivities(intent, 0); } - public static Intent getAppWithUniqueLabel(List drawerApps, String label){ - String errorMessage = "No unique app found named \"" + label + "\"\n"; + public static Intent getIntentFromAppDrawer(String query, boolean isPackageName){ + String errorMessage = isPackageName ? + "No launchable app with package name \"" + query + "\" found from app drawer" : + "No unique launchable app named \"" + query + "\" found from app drawer"; + String orgQuery = query; + query = query.toLowerCase(Locale.getDefault()); + + List exactMatchesLabel = new ArrayList<>(); + List potentialMatchesAppName = new ArrayList<>(); + List potentialMatchesPkgName = new ArrayList<>(); Context context = FakeContext.get(); - label = label.toLowerCase(Locale.getDefault()); - - List exactMatches = new ArrayList<>(); - List potentialMatches = new ArrayList<>(); - - for (ResolveInfo drawerApp : drawerApps) { - String appName = drawerApp.loadLabel(context.getPackageManager()).toString(); - if (appName.toLowerCase(Locale.getDefault()).equals(label)){ - exactMatches.add(drawerApp); - }else if (appName.toLowerCase(Locale.getDefault()) - .contains(label.toLowerCase(Locale.getDefault()))) { - potentialMatches.add(drawerApp); + PackageManager pm = context.getPackageManager(); + + for (ResolveInfo drawerApp : getDrawerApps()) { + String packageName = drawerApp.activityInfo.packageName; + String label = drawerApp.loadLabel(pm).toString().toLowerCase(Locale.getDefault()); + + if (isPackageName){ + if (packageName.equals(query)) { + ComponentName componentName = new ComponentName(packageName, drawerApp.activityInfo.name); + return new Intent().setComponent(componentName) + .putExtra("APP_LABEL", drawerApp.loadLabel(pm).toString()); + } else if (packageName.contains(query)){ + potentialMatchesPkgName.add(drawerApp); + } + } else { + if (label.equals(query)) { + exactMatchesLabel.add(drawerApp); + } else if (label.contains(query)){ + potentialMatchesAppName.add(drawerApp); + } } } - if (exactMatches.size() == 1){ - ComponentName componentName = new ComponentName(exactMatches.get(0).activityInfo.packageName, exactMatches.get(0).activityInfo.name); - return new Intent().setComponent(componentName) - .putExtra("APP_LABEL", exactMatches.get(0).loadLabel(context.getPackageManager()).toString()); - } else{ - String suggestions = ""; + Intent launchIntent = processResolvedLists(pm,isPackageName,false,errorMessage,exactMatchesLabel,potentialMatchesAppName,potentialMatchesPkgName); - if (!exactMatches.isEmpty()){ - suggestions+=LogUtils.buildAppListMessage("Found "+exactMatches.size()+" exact matches:",exactMatches)+"\n"; - } - if (!potentialMatches.isEmpty()){ - suggestions+=LogUtils.buildAppListMessage("Found " + potentialMatches.size() + " potential " + (potentialMatches.size() == 1 ? "match:" : "matches:"), potentialMatches)+"\n"; - } + if (launchIntent == null){ + Ln.w("Trying to find from list of all apps"); + return getIntentFromListOfAllApps(orgQuery,isPackageName); + } + else if (launchIntent.getBooleanExtra("MULTIPLE_EXACT_LABELS", false)) { + //Let processResolvedLists() return a "garbage" intent if there are multiple exact labels. + // We want to avoid redundant check. This happens if first check "launchIntent == null" + // becomes true since getIntentFromListOfAllApps() also calls processResolvedLists(). + // This limitation can be removed + return null; + } + return launchIntent; + } + + @SuppressLint("QueryPermissionsNeeded") + public static Intent getIntentFromListOfAllApps(String query, boolean isPackageName){ + String errorMessage = isPackageName ? + "No launchable app with package name \"" + query + "\" found from list of all apps" : + "No unique launchable app named \"" + query + "\" found from list of all apps"; + query = query.toLowerCase(Locale.getDefault()); + + List exactMatchesLabel = new ArrayList<>(); + List potentialMatchesAppName = new ArrayList<>(); + List potentialMatchesPkgName = new ArrayList<>(); + Context context = FakeContext.get(); + PackageManager pm = context.getPackageManager(); + ResolveInfo resolveInfo; + + boolean isTV = false; + UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE); + if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { + isTV = true; + } - if (!suggestions.isEmpty()){ - Ln.e(errorMessage+suggestions); + for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) { + String packageName = appInfo.packageName; + String label = appInfo.loadLabel(pm).toString().toLowerCase(Locale.getDefault()); + Intent launchIntent = isTV ? + pm.getLeanbackLaunchIntentForPackage(packageName) : + pm.getLaunchIntentForPackage(packageName); + + if (isPackageName){ + if (packageName.equals(query) && launchIntent == null) { + Ln.e("No launch intent for " + appInfo.loadLabel(pm) + " ["+packageName+"]"); + return null; + } else if (packageName.equals(query)) { + return launchIntent.putExtra("APP_LABEL", label); + }else if (packageName.contains(query) && launchIntent != null){ + resolveInfo = pm.resolveActivity(launchIntent, 0); + potentialMatchesPkgName.add(resolveInfo); + } + } else { + if (launchIntent == null) { + if (label.equals(query)){ + Ln.w("Ignoring "+ appInfo.loadLabel(pm) + " ["+packageName+"] which has no launch intent"); + } + continue; + } + resolveInfo = pm.resolveActivity(launchIntent, 0); + if (label.equals(query)) { + exactMatchesLabel.add(resolveInfo); + } else if (label.contains(query)){ + potentialMatchesAppName.add(resolveInfo); + } } - return null; } + + return processResolvedLists(pm,isPackageName,true,errorMessage,exactMatchesLabel,potentialMatchesAppName,potentialMatchesPkgName); } - public static Intent getAppGivenPackageName(List drawerApps, String packageName){ - packageName = packageName.toLowerCase(Locale.getDefault()); - String errorMessage = "No app found with package name \"" + packageName + "\"\n"; - List potentialMatches = new ArrayList<>(); - for (ResolveInfo drawerApp : drawerApps) { - if (drawerApp.activityInfo.packageName.equals(packageName)){ - ComponentName componentName = new ComponentName(drawerApp.activityInfo.packageName, drawerApp.activityInfo.name); - return new Intent().setComponent(componentName); - } else if (drawerApp.activityInfo.packageName - .contains(packageName)) { - potentialMatches.add(drawerApp); + private static Intent processResolvedLists(PackageManager pm, boolean isPackageName, boolean showSuggestions, String errorMessage, + List exactMatchesLabel, + List potentialMatchesAppName, + List potentialMatchesPkgName){ + String suggestions = "\n"; + boolean multipleExactLabelMatches = false; + if (isPackageName){ + if (!potentialMatchesPkgName.isEmpty()){ + suggestions+=LogUtils.buildAppListMessage("Found "+potentialMatchesPkgName.size()+" potential matches:",potentialMatchesPkgName); + } + } else { + if (exactMatchesLabel.size() == 1){ + ActivityInfo activityInfo = exactMatchesLabel.get(0).activityInfo; + ComponentName componentName = new ComponentName(activityInfo.packageName, activityInfo.name); + return new Intent().setComponent(componentName) + .putExtra("APP_LABEL", exactMatchesLabel.get(0).loadLabel(pm).toString()); + } else{ + if (!exactMatchesLabel.isEmpty()){ + multipleExactLabelMatches = true; + showSuggestions = true; + suggestions+=LogUtils.buildAppListMessage("Found "+exactMatchesLabel.size()+" exact matches:",exactMatchesLabel)+"\n"; + } + if (!potentialMatchesAppName.isEmpty()){ + suggestions+=LogUtils.buildAppListMessage("Found " + potentialMatchesAppName.size() + " other potential " + (potentialMatchesAppName.size() == 1 ? "match:" : "matches:"), potentialMatchesAppName)+"\n"; + } } } - if (!potentialMatches.isEmpty()){ - Ln.e(errorMessage+LogUtils.buildAppListMessage("Found " + potentialMatches.size() + " potential " + (potentialMatches.size() == 1 ? "match:" : "matches:"), potentialMatches)); + + if (showSuggestions){ + Ln.e(errorMessage + (suggestions.equals("\n")? "\0" : suggestions)); } else { Ln.e(errorMessage); } - return null; + if (multipleExactLabelMatches){ + return new Intent().putExtra("MULTIPLE_EXACT_LABELS", true); + } else { + return null; + } } } + +