Skip to content

Commit a6e38b7

Browse files
committed
Speed up app launches by prioritizing drawer apps.
If not found, find from list of all apps.
1 parent 3d441c9 commit a6e38b7

File tree

2 files changed

+137
-52
lines changed

2 files changed

+137
-52
lines changed

server/src/main/java/com/genymobile/scrcpy/control/Controller.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import android.content.ComponentName;
1919
import android.content.IOnPrimaryClipChangedListener;
2020
import android.content.Intent;
21-
import android.content.pm.ResolveInfo;
2221
import android.os.Build;
2322
import android.os.SystemClock;
2423
import android.view.InputDevice;
@@ -27,7 +26,6 @@
2726
import android.view.MotionEvent;
2827

2928
import java.io.IOException;
30-
import java.util.List;
3129
import java.util.concurrent.ExecutorService;
3230
import java.util.concurrent.Executors;
3331
import java.util.concurrent.ScheduledExecutorService;
@@ -639,19 +637,17 @@ private void startApp(String name) {
639637

640638
Ln.i("Starting activity: " + name);
641639
} else {
642-
List<ResolveInfo> drawerApps = Device.getDrawerApps();
643-
644640
boolean searchByName = name.startsWith("?") || name.contains(" ");
645641
if (searchByName) {
646642
if (name.contains("?")) {
647643
name = name.substring(1);
648644
}
649-
launchIntent = Device.getAppWithUniqueLabel(drawerApps,name);
645+
launchIntent = Device.getIntentFromAppDrawer(name,false);
650646
if (launchIntent == null){
651647
return;
652648
}
653649
} else {
654-
launchIntent = Device.getAppGivenPackageName(drawerApps,name);
650+
launchIntent = Device.getIntentFromAppDrawer(name,true);
655651
if (launchIntent == null) {
656652
return;
657653
}

server/src/main/java/com/genymobile/scrcpy/device/Device.java

+135-46
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import android.content.Context;
1919
import android.content.Intent;
2020
import android.app.ActivityOptions;
21+
import android.content.pm.ActivityInfo;
22+
import android.content.pm.ApplicationInfo;
2123
import android.content.pm.PackageManager;
2224
import android.content.pm.ResolveInfo;
2325
import android.content.res.Configuration;
@@ -244,8 +246,7 @@ public static void startApp(Intent launchIntent, int displayId, boolean forceSto
244246
@SuppressLint("QueryPermissionsNeeded")
245247
public static List<ResolveInfo> getDrawerApps() {
246248
Context context = FakeContext.get();
247-
PackageManager packageManager = context.getPackageManager();
248-
249+
PackageManager pm = context.getPackageManager();
249250
Intent intent = new Intent(Intent.ACTION_MAIN, null);
250251

251252
UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
@@ -254,66 +255,154 @@ public static List<ResolveInfo> getDrawerApps() {
254255
} else {
255256
intent.addCategory(Intent.CATEGORY_LAUNCHER);
256257
}
257-
return packageManager.queryIntentActivities(intent, 0);
258+
return pm.queryIntentActivities(intent, 0);
258259
}
259260

260-
public static Intent getAppWithUniqueLabel(List<ResolveInfo> drawerApps, String label){
261-
String errorMessage = "No unique app found named \"" + label + "\"\n";
261+
public static Intent getIntentFromAppDrawer(String query, boolean isPackageName){
262+
String errorMessage = isPackageName ?
263+
"No launchable app with package name \"" + query + "\" found from app drawer" :
264+
"No unique launchable app named \"" + query + "\" found from app drawer";
265+
String orgQuery = query;
266+
query = query.toLowerCase(Locale.getDefault());
267+
268+
List<ResolveInfo> exactMatchesLabel = new ArrayList<>();
269+
List<ResolveInfo> potentialMatchesAppName = new ArrayList<>();
270+
List<ResolveInfo> potentialMatchesPkgName = new ArrayList<>();
262271
Context context = FakeContext.get();
263-
label = label.toLowerCase(Locale.getDefault());
264-
265-
List<ResolveInfo> exactMatches = new ArrayList<>();
266-
List<ResolveInfo> potentialMatches = new ArrayList<>();
267-
268-
for (ResolveInfo drawerApp : drawerApps) {
269-
String appName = drawerApp.loadLabel(context.getPackageManager()).toString();
270-
if (appName.toLowerCase(Locale.getDefault()).equals(label)){
271-
exactMatches.add(drawerApp);
272-
}else if (appName.toLowerCase(Locale.getDefault())
273-
.contains(label.toLowerCase(Locale.getDefault()))) {
274-
potentialMatches.add(drawerApp);
272+
PackageManager pm = context.getPackageManager();
273+
274+
for (ResolveInfo drawerApp : getDrawerApps()) {
275+
String packageName = drawerApp.activityInfo.packageName;
276+
String label = drawerApp.loadLabel(pm).toString().toLowerCase(Locale.getDefault());
277+
278+
if (isPackageName){
279+
if (packageName.equals(query)) {
280+
ComponentName componentName = new ComponentName(packageName, drawerApp.activityInfo.name);
281+
return new Intent().setComponent(componentName)
282+
.putExtra("APP_LABEL", drawerApp.loadLabel(pm).toString());
283+
} else if (packageName.contains(query)){
284+
potentialMatchesPkgName.add(drawerApp);
285+
}
286+
} else {
287+
if (label.equals(query)) {
288+
exactMatchesLabel.add(drawerApp);
289+
} else if (label.contains(query)){
290+
potentialMatchesAppName.add(drawerApp);
291+
}
275292
}
276293
}
277294

278-
if (exactMatches.size() == 1){
279-
ComponentName componentName = new ComponentName(exactMatches.get(0).activityInfo.packageName, exactMatches.get(0).activityInfo.name);
280-
return new Intent().setComponent(componentName)
281-
.putExtra("APP_LABEL", exactMatches.get(0).loadLabel(context.getPackageManager()).toString());
282-
} else{
283-
String suggestions = "";
295+
Intent launchIntent = processResolvedLists(pm,isPackageName,false,errorMessage,exactMatchesLabel,potentialMatchesAppName,potentialMatchesPkgName);
284296

285-
if (!exactMatches.isEmpty()){
286-
suggestions+=LogUtils.buildAppListMessage("Found "+exactMatches.size()+" exact matches:",exactMatches)+"\n";
287-
}
288-
if (!potentialMatches.isEmpty()){
289-
suggestions+=LogUtils.buildAppListMessage("Found " + potentialMatches.size() + " potential " + (potentialMatches.size() == 1 ? "match:" : "matches:"), potentialMatches)+"\n";
290-
}
297+
if (launchIntent == null){
298+
Ln.w("Trying to find from list of all apps");
299+
return getIntentFromListOfAllApps(orgQuery,isPackageName);
300+
}
301+
else if (launchIntent.getBooleanExtra("MULTIPLE_EXACT_LABELS", false)) {
302+
//Let processResolvedLists() return a "garbage" intent if there are multiple exact labels.
303+
// We want to avoid redundant check. This happens if first check "launchIntent == null"
304+
// becomes true since getIntentFromListOfAllApps() also calls processResolvedLists().
305+
// This limitation can be removed
306+
return null;
307+
}
308+
return launchIntent;
309+
}
310+
311+
@SuppressLint("QueryPermissionsNeeded")
312+
public static Intent getIntentFromListOfAllApps(String query, boolean isPackageName){
313+
String errorMessage = isPackageName ?
314+
"No launchable app with package name \"" + query + "\" found from list of all apps" :
315+
"No unique launchable app named \"" + query + "\" found from list of all apps";
316+
query = query.toLowerCase(Locale.getDefault());
317+
318+
List<ResolveInfo> exactMatchesLabel = new ArrayList<>();
319+
List<ResolveInfo> potentialMatchesAppName = new ArrayList<>();
320+
List<ResolveInfo> potentialMatchesPkgName = new ArrayList<>();
321+
Context context = FakeContext.get();
322+
PackageManager pm = context.getPackageManager();
323+
ResolveInfo resolveInfo;
324+
325+
boolean isTV = false;
326+
UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
327+
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
328+
isTV = true;
329+
}
291330

292-
if (!suggestions.isEmpty()){
293-
Ln.e(errorMessage+suggestions);
331+
for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) {
332+
String packageName = appInfo.packageName;
333+
String label = appInfo.loadLabel(pm).toString().toLowerCase(Locale.getDefault());
334+
Intent launchIntent = isTV ?
335+
pm.getLeanbackLaunchIntentForPackage(packageName) :
336+
pm.getLaunchIntentForPackage(packageName);
337+
338+
if (isPackageName){
339+
if (packageName.equals(query) && launchIntent == null) {
340+
Ln.e("No launch intent for " + appInfo.loadLabel(pm) + " ["+packageName+"]");
341+
return null;
342+
} else if (packageName.equals(query)) {
343+
return launchIntent.putExtra("APP_LABEL", label);
344+
}else if (packageName.contains(query) && launchIntent != null){
345+
resolveInfo = pm.resolveActivity(launchIntent, 0);
346+
potentialMatchesPkgName.add(resolveInfo);
347+
}
348+
} else {
349+
if (launchIntent == null) {
350+
if (label.equals(query)){
351+
Ln.w("Ignoring "+ appInfo.loadLabel(pm) + " ["+packageName+"] which has no launch intent");
352+
}
353+
continue;
354+
}
355+
resolveInfo = pm.resolveActivity(launchIntent, 0);
356+
if (label.equals(query)) {
357+
exactMatchesLabel.add(resolveInfo);
358+
} else if (label.contains(query)){
359+
potentialMatchesAppName.add(resolveInfo);
360+
}
294361
}
295-
return null;
296362
}
363+
364+
return processResolvedLists(pm,isPackageName,true,errorMessage,exactMatchesLabel,potentialMatchesAppName,potentialMatchesPkgName);
297365
}
298366

299-
public static Intent getAppGivenPackageName(List<ResolveInfo> drawerApps, String packageName){
300-
packageName = packageName.toLowerCase(Locale.getDefault());
301-
String errorMessage = "No app found with package name \"" + packageName + "\"\n";
302-
List<ResolveInfo> potentialMatches = new ArrayList<>();
303-
for (ResolveInfo drawerApp : drawerApps) {
304-
if (drawerApp.activityInfo.packageName.equals(packageName)){
305-
ComponentName componentName = new ComponentName(drawerApp.activityInfo.packageName, drawerApp.activityInfo.name);
306-
return new Intent().setComponent(componentName);
307-
} else if (drawerApp.activityInfo.packageName
308-
.contains(packageName)) {
309-
potentialMatches.add(drawerApp);
367+
private static Intent processResolvedLists(PackageManager pm, boolean isPackageName, boolean showSuggestions, String errorMessage,
368+
List<ResolveInfo> exactMatchesLabel,
369+
List<ResolveInfo> potentialMatchesAppName,
370+
List<ResolveInfo> potentialMatchesPkgName){
371+
String suggestions = "\n";
372+
boolean multipleExactLabelMatches = false;
373+
if (isPackageName){
374+
if (!potentialMatchesPkgName.isEmpty()){
375+
suggestions+=LogUtils.buildAppListMessage("Found "+potentialMatchesPkgName.size()+" potential matches:",potentialMatchesPkgName);
376+
}
377+
} else {
378+
if (exactMatchesLabel.size() == 1){
379+
ActivityInfo activityInfo = exactMatchesLabel.get(0).activityInfo;
380+
ComponentName componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
381+
return new Intent().setComponent(componentName)
382+
.putExtra("APP_LABEL", exactMatchesLabel.get(0).loadLabel(pm).toString());
383+
} else{
384+
if (!exactMatchesLabel.isEmpty()){
385+
multipleExactLabelMatches = true;
386+
showSuggestions = true;
387+
suggestions+=LogUtils.buildAppListMessage("Found "+exactMatchesLabel.size()+" exact matches:",exactMatchesLabel)+"\n";
388+
}
389+
if (!potentialMatchesAppName.isEmpty()){
390+
suggestions+=LogUtils.buildAppListMessage("Found " + potentialMatchesAppName.size() + " other potential " + (potentialMatchesAppName.size() == 1 ? "match:" : "matches:"), potentialMatchesAppName)+"\n";
391+
}
310392
}
311393
}
312-
if (!potentialMatches.isEmpty()){
313-
Ln.e(errorMessage+LogUtils.buildAppListMessage("Found " + potentialMatches.size() + " potential " + (potentialMatches.size() == 1 ? "match:" : "matches:"), potentialMatches));
394+
395+
if (showSuggestions){
396+
Ln.e(errorMessage + (suggestions.equals("\n")? "\0" : suggestions));
314397
} else {
315398
Ln.e(errorMessage);
316399
}
317-
return null;
400+
if (multipleExactLabelMatches){
401+
return new Intent().putExtra("MULTIPLE_EXACT_LABELS", true);
402+
} else {
403+
return null;
404+
}
318405
}
319406
}
407+
408+

0 commit comments

Comments
 (0)