mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-19 19:15:08 +00:00
Merge 9d69d7eeb7
into 1a0d300786
This commit is contained in:
commit
0936a158dc
5 changed files with 293 additions and 182 deletions
|
@ -5,12 +5,10 @@ import com.genymobile.scrcpy.AsyncProcessor;
|
|||
import com.genymobile.scrcpy.CleanUp;
|
||||
import com.genymobile.scrcpy.Options;
|
||||
import com.genymobile.scrcpy.device.Device;
|
||||
import com.genymobile.scrcpy.device.DeviceApp;
|
||||
import com.genymobile.scrcpy.device.Point;
|
||||
import com.genymobile.scrcpy.device.Position;
|
||||
import com.genymobile.scrcpy.device.Size;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.util.LogUtils;
|
||||
import com.genymobile.scrcpy.video.SurfaceCapture;
|
||||
import com.genymobile.scrcpy.video.VirtualDisplayListener;
|
||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||
|
@ -28,7 +26,6 @@ import android.view.KeyEvent;
|
|||
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;
|
||||
|
@ -627,37 +624,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||
}
|
||||
|
||||
private void startApp(String name) {
|
||||
boolean forceStopBeforeStart = name.startsWith("+");
|
||||
if (forceStopBeforeStart) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
DeviceApp app;
|
||||
boolean searchByName = name.startsWith("?");
|
||||
if (searchByName) {
|
||||
name = name.substring(1);
|
||||
|
||||
Ln.i("Processing Android apps... (this may take some time)");
|
||||
List<DeviceApp> apps = Device.findByName(name);
|
||||
if (apps.isEmpty()) {
|
||||
Ln.w("No app found for name \"" + name + "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (apps.size() > 1) {
|
||||
String title = "No unique app found for name \"" + name + "\":";
|
||||
Ln.w(LogUtils.buildAppListMessage(title, apps));
|
||||
return;
|
||||
}
|
||||
|
||||
app = apps.get(0);
|
||||
} else {
|
||||
app = Device.findByPackageName(name);
|
||||
if (app == null) {
|
||||
Ln.w("No app found for package \"" + name + "\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Intent launchIntent = new Intent();
|
||||
|
||||
int startAppDisplayId = getStartAppDisplayId();
|
||||
if (startAppDisplayId == Device.DISPLAY_ID_NONE) {
|
||||
|
@ -665,8 +632,55 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||
return;
|
||||
}
|
||||
|
||||
Ln.i("Starting app \"" + app.getName() + "\" [" + app.getPackageName() + "] on display " + startAppDisplayId + "...");
|
||||
Device.startApp(app.getPackageName(), startAppDisplayId, forceStopBeforeStart);
|
||||
if (name.contains("+") && name.contains("-")){
|
||||
Ln.e("Can't make a (+) new instance if (-) force stop is also specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean newInstance = name.startsWith("+");
|
||||
if (newInstance) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
boolean forceStopBeforeStart = name.startsWith("-");
|
||||
if (forceStopBeforeStart) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
if (name.contains("/")){
|
||||
launchIntent = Device.getIntentFromClassName(name.split("/")[0], name.split("/")[1]);
|
||||
|
||||
if (launchIntent == null) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
boolean searchByName = name.startsWith("?") || name.contains(" ");
|
||||
if (searchByName) {
|
||||
if (name.contains("?")) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
launchIntent = Device.getIntentFromAppDrawer(name,false);
|
||||
if (launchIntent == null){
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
launchIntent = Device.getIntentFromAppDrawer(name,true);
|
||||
if (launchIntent == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String packageName = launchIntent.getComponent().getPackageName();
|
||||
String label = launchIntent.getStringExtra("APP_LABEL");
|
||||
launchIntent.removeExtra("APP_LABEL");
|
||||
Ln.i("Starting app \"" + label + "\" [" + packageName + "] on display " + startAppDisplayId + "...");
|
||||
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (newInstance) {
|
||||
launchIntent.addFlags(Intent. FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
}
|
||||
Device.startApp(launchIntent, startAppDisplayId, forceStopBeforeStart);
|
||||
}
|
||||
|
||||
private int getStartAppDisplayId() {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy.device;
|
|||
|
||||
import com.genymobile.scrcpy.AndroidVersions;
|
||||
import com.genymobile.scrcpy.FakeContext;
|
||||
import com.genymobile.scrcpy.util.AppListProcessor;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.wrappers.ActivityManager;
|
||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||
|
@ -12,10 +13,15 @@ import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
|||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.UiModeManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
@ -25,7 +31,6 @@ import android.view.InputEvent;
|
|||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -221,84 +226,7 @@ public final class Device {
|
|||
return displayInfo.getRotation();
|
||||
}
|
||||
|
||||
public static List<DeviceApp> listApps() {
|
||||
List<DeviceApp> apps = new ArrayList<>();
|
||||
PackageManager pm = FakeContext.get().getPackageManager();
|
||||
for (ApplicationInfo appInfo : getLaunchableApps(pm)) {
|
||||
apps.add(toApp(pm, appInfo));
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private static List<ApplicationInfo> getLaunchableApps(PackageManager pm) {
|
||||
List<ApplicationInfo> result = new ArrayList<>();
|
||||
for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) {
|
||||
if (appInfo.enabled && getLaunchIntent(pm, appInfo.packageName) != null) {
|
||||
result.add(appInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Intent getLaunchIntent(PackageManager pm, String packageName) {
|
||||
Intent launchIntent = pm.getLaunchIntentForPackage(packageName);
|
||||
if (launchIntent != null) {
|
||||
return launchIntent;
|
||||
}
|
||||
|
||||
return pm.getLeanbackLaunchIntentForPackage(packageName);
|
||||
}
|
||||
|
||||
private static DeviceApp toApp(PackageManager pm, ApplicationInfo appInfo) {
|
||||
String name = pm.getApplicationLabel(appInfo).toString();
|
||||
boolean system = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
return new DeviceApp(appInfo.packageName, name, system);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static DeviceApp findByPackageName(String packageName) {
|
||||
PackageManager pm = FakeContext.get().getPackageManager();
|
||||
// No need to filter by "launchable" apps, an error will be reported on start if the app is not launchable
|
||||
for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) {
|
||||
if (packageName.equals(appInfo.packageName)) {
|
||||
return toApp(pm, appInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static List<DeviceApp> findByName(String searchName) {
|
||||
List<DeviceApp> result = new ArrayList<>();
|
||||
searchName = searchName.toLowerCase(Locale.getDefault());
|
||||
|
||||
PackageManager pm = FakeContext.get().getPackageManager();
|
||||
for (ApplicationInfo appInfo : getLaunchableApps(pm)) {
|
||||
String name = pm.getApplicationLabel(appInfo).toString();
|
||||
if (name.toLowerCase(Locale.getDefault()).startsWith(searchName)) {
|
||||
boolean system = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
result.add(new DeviceApp(appInfo.packageName, name, system));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void startApp(String packageName, int displayId, boolean forceStop) {
|
||||
PackageManager pm = FakeContext.get().getPackageManager();
|
||||
|
||||
Intent launchIntent = getLaunchIntent(pm, packageName);
|
||||
if (launchIntent == null) {
|
||||
Ln.w("Cannot create launch intent for app " + packageName);
|
||||
return;
|
||||
}
|
||||
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
public static void startApp(Intent launchIntent, int displayId, boolean forceStop) {
|
||||
Bundle options = null;
|
||||
if (Build.VERSION.SDK_INT >= AndroidVersions.API_26_ANDROID_8_0) {
|
||||
ActivityOptions launchOptions = ActivityOptions.makeBasic();
|
||||
|
@ -308,8 +236,131 @@ public final class Device {
|
|||
|
||||
ActivityManager am = ServiceManager.getActivityManager();
|
||||
if (forceStop) {
|
||||
am.forceStopPackage(packageName);
|
||||
am.forceStopPackage(launchIntent.getComponent().getPackageName());
|
||||
}
|
||||
am.startActivity(launchIntent, options);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static List<ResolveInfo> getDrawerApps() {
|
||||
Context context = FakeContext.get();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN, null);
|
||||
|
||||
UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
|
||||
intent.addCategory(uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION?
|
||||
Intent.CATEGORY_LEANBACK_LAUNCHER : Intent.CATEGORY_LAUNCHER);
|
||||
|
||||
return pm.queryIntentActivities(intent, 0);
|
||||
}
|
||||
|
||||
public static Intent getIntentFromAppDrawer(String query, boolean isPackageName){
|
||||
AppListProcessor appListProcessor = new AppListProcessor(isPackageName, true, query);
|
||||
query = query.toLowerCase(Locale.getDefault());
|
||||
Context context = FakeContext.get();
|
||||
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)){
|
||||
appListProcessor.addPotentialMatchesPkgName(drawerApp);
|
||||
}
|
||||
} else {
|
||||
if (label.equals(query)) {
|
||||
appListProcessor.addExactMatchesLabel(drawerApp);
|
||||
} else if (label.contains(query)){
|
||||
appListProcessor.addPotentialMatchesAppName(drawerApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Suggestions will show regardless if MULTIPLE_EXACT_LABELS
|
||||
Intent launchIntent = appListProcessor.getIntent(pm,false);
|
||||
if (launchIntent == null){
|
||||
Ln.w("Trying to find from list of all apps");
|
||||
return getIntentFromListOfAllApps(appListProcessor.getOrgQuery(), 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 to instead list possible exact label matches
|
||||
// from list of all apps also.
|
||||
return null;
|
||||
}
|
||||
return launchIntent;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static Intent getIntentFromListOfAllApps(String query, boolean isPackageName){
|
||||
AppListProcessor appListProcessor = new AppListProcessor(isPackageName, false, query);
|
||||
query = query.toLowerCase(Locale.getDefault());
|
||||
Context context = FakeContext.get();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
boolean isTV = false;
|
||||
UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
|
||||
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
|
||||
isTV = true;
|
||||
}
|
||||
|
||||
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){
|
||||
appListProcessor.addPotentialMatchesPkgName(pm.resolveActivity(launchIntent, 0));
|
||||
}
|
||||
} else {
|
||||
if (launchIntent == null) {
|
||||
if (label.equals(query)){
|
||||
Ln.w("Ignoring "+ appInfo.loadLabel(pm) + " ["+packageName+"] which has no launch intent");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ResolveInfo resolveInfo = pm.resolveActivity(launchIntent, 0);
|
||||
if (label.equals(query)) {
|
||||
appListProcessor.addExactMatchesLabel(resolveInfo);
|
||||
} else if (label.contains(query)){
|
||||
appListProcessor.addPotentialMatchesAppName(resolveInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appListProcessor.getIntent(pm,true);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static Intent getIntentFromClassName(String packageName, String activityName){
|
||||
Context context = FakeContext.get();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName(packageName, activityName);
|
||||
List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
|
||||
if (list.size() == 1){
|
||||
intent.putExtra("APP_LABEL", list.get(0).loadLabel(pm).toString());
|
||||
Ln.i("Activity class {"+packageName+"/"+activityName+"} found");
|
||||
return intent;
|
||||
} else {
|
||||
Ln.e("Activity class {"+packageName+"/"+activityName+"} does not exist");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package com.genymobile.scrcpy.device;
|
||||
|
||||
public final class DeviceApp {
|
||||
|
||||
private final String packageName;
|
||||
private final String name;
|
||||
private final boolean system;
|
||||
|
||||
public DeviceApp(String packageName, String name, boolean system) {
|
||||
this.packageName = packageName;
|
||||
this.name = name;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSystem() {
|
||||
return system;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.genymobile.scrcpy.util;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AppListProcessor {
|
||||
private final List<ResolveInfo> exactMatchesLabel = new ArrayList<>();
|
||||
private final List<ResolveInfo> potentialMatchesAppName = new ArrayList<>();
|
||||
private final List<ResolveInfo> potentialMatchesPkgName = new ArrayList<>();
|
||||
private final boolean isPackageName; //Treated as label if false
|
||||
private final String errorMessage;
|
||||
private final String query;
|
||||
|
||||
public AppListProcessor(boolean isPackageName, boolean appDrawer, String orgQuery) {
|
||||
this.isPackageName = isPackageName;
|
||||
this.query = orgQuery;
|
||||
|
||||
String tmpErrMsg = isPackageName ? "No launchable app with package name " : "No unique launchable app named ";
|
||||
this.errorMessage = tmpErrMsg + "\"" + orgQuery + "\" found from " + ( appDrawer ? "app drawer" : "list of all apps");
|
||||
}
|
||||
|
||||
public String getOrgQuery(){
|
||||
return query;
|
||||
}
|
||||
|
||||
public void addExactMatchesLabel(ResolveInfo resolveInfo) {
|
||||
exactMatchesLabel.add(resolveInfo);
|
||||
}
|
||||
|
||||
public void addPotentialMatchesAppName(ResolveInfo resolveInfo) {
|
||||
potentialMatchesAppName.add(resolveInfo);
|
||||
}
|
||||
|
||||
public void addPotentialMatchesPkgName(ResolveInfo resolveInfo) {
|
||||
potentialMatchesPkgName.add(resolveInfo);
|
||||
}
|
||||
|
||||
public Intent getIntent(PackageManager pm, boolean showSuggestions){
|
||||
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 (showSuggestions){
|
||||
Ln.e(errorMessage + (suggestions.equals("\n")? "\0" : suggestions));
|
||||
} else {
|
||||
Ln.e(errorMessage);
|
||||
}
|
||||
if (multipleExactLabelMatches){
|
||||
return new Intent().putExtra("MULTIPLE_EXACT_LABELS", true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
package com.genymobile.scrcpy.util;
|
||||
|
||||
import com.genymobile.scrcpy.AndroidVersions;
|
||||
import com.genymobile.scrcpy.FakeContext;
|
||||
import com.genymobile.scrcpy.audio.AudioCodec;
|
||||
import com.genymobile.scrcpy.device.Device;
|
||||
import com.genymobile.scrcpy.device.DeviceApp;
|
||||
import com.genymobile.scrcpy.device.DisplayInfo;
|
||||
import com.genymobile.scrcpy.device.Size;
|
||||
import com.genymobile.scrcpy.video.VideoCodec;
|
||||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
|
@ -23,10 +24,12 @@ import android.media.MediaCodecList;
|
|||
import android.os.Build;
|
||||
import android.util.Range;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public final class LogUtils {
|
||||
|
@ -215,54 +218,43 @@ public final class LogUtils {
|
|||
|
||||
|
||||
public static String buildAppListMessage() {
|
||||
List<DeviceApp> apps = Device.listApps();
|
||||
return buildAppListMessage("List of apps:", apps);
|
||||
List<ResolveInfo> drawerApps = Device.getDrawerApps();
|
||||
return buildAppListMessage("List of apps:", drawerApps);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static String buildAppListMessage(String title, List<DeviceApp> apps) {
|
||||
StringBuilder builder = new StringBuilder(title);
|
||||
public static String buildAppListMessage(String title, List<ResolveInfo> drawerApps) {
|
||||
Map<String, String> appMap = new HashMap<>();
|
||||
Context context = FakeContext.get();
|
||||
|
||||
// Sort by:
|
||||
// 1. system flag (system apps are before non-system apps)
|
||||
// 2. name
|
||||
// 3. package name
|
||||
// Comparator.comparing() was introduced in API 24, so it cannot be used here to simplify the code
|
||||
Collections.sort(apps, (thisApp, otherApp) -> {
|
||||
// System apps first
|
||||
int cmp = -Boolean.compare(thisApp.isSystem(), otherApp.isSystem());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
for (ResolveInfo app : drawerApps) {
|
||||
appMap.put(app.activityInfo.packageName, app.loadLabel(context.getPackageManager()).toString());
|
||||
}
|
||||
|
||||
TreeMap<String, String> sortedMap = new TreeMap<>(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String key1, String key2) {
|
||||
int labelComparison = appMap.get(key1).compareToIgnoreCase(appMap.get(key2));
|
||||
if (labelComparison != 0) {
|
||||
return labelComparison;
|
||||
}
|
||||
return key1.compareTo(key2); // Compare by package name if labels are the same
|
||||
}
|
||||
|
||||
cmp = Objects.compare(thisApp.getName(), otherApp.getName(), String::compareTo);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return Objects.compare(thisApp.getPackageName(), otherApp.getPackageName(), String::compareTo);
|
||||
});
|
||||
sortedMap.putAll(appMap);
|
||||
|
||||
StringBuilder builder = new StringBuilder(title);
|
||||
final int column = 30;
|
||||
for (DeviceApp app : apps) {
|
||||
String name = app.getName();
|
||||
int padding = column - name.length();
|
||||
builder.append("\n ");
|
||||
if (app.isSystem()) {
|
||||
builder.append("* ");
|
||||
} else {
|
||||
builder.append("- ");
|
||||
|
||||
}
|
||||
builder.append(name);
|
||||
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
|
||||
int padding = column - entry.getValue().length();
|
||||
builder.append("\n - ");
|
||||
builder.append(entry.getValue());
|
||||
if (padding > 0) {
|
||||
builder.append(String.format("%" + padding + "s", " "));
|
||||
} else {
|
||||
builder.append("\n ").append(String.format("%" + column + "s", " "));
|
||||
builder.append("\n - ").append(String.format("%" + column + "s", " "));
|
||||
}
|
||||
builder.append(" ").append(app.getPackageName());
|
||||
builder.append(" ").append(entry.getKey());
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue