Add --list-apps
Add an option to list all apps installed on the device: scrcpy --list-apps
This commit is contained in:
parent
1ea229fc2c
commit
6ff3d9b571
@ -33,6 +33,7 @@ _scrcpy() {
|
||||
--keyboard=
|
||||
--kill-adb-on-close
|
||||
--legacy-paste
|
||||
--list-apps
|
||||
--list-camera-sizes
|
||||
--list-cameras
|
||||
--list-displays
|
||||
|
@ -40,6 +40,7 @@ arguments=(
|
||||
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
|
||||
'--kill-adb-on-close[Kill adb when scrcpy terminates]'
|
||||
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
|
||||
'--list-apps[List Android apps installed on the device]'
|
||||
'--list-camera-sizes[List the valid camera capture sizes]'
|
||||
'--list-cameras[List cameras available on the device]'
|
||||
'--list-displays[List displays available on the device]'
|
||||
|
@ -227,6 +227,10 @@ Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+S
|
||||
|
||||
This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically.
|
||||
|
||||
.TP
|
||||
.B \-\-list\-apps
|
||||
List Android apps installed on the device.
|
||||
|
||||
.TP
|
||||
.B \-\-list\-camera\-sizes
|
||||
List the valid camera capture sizes.
|
||||
|
@ -103,6 +103,7 @@ enum {
|
||||
OPT_AUDIO_DUP,
|
||||
OPT_GAMEPAD,
|
||||
OPT_NEW_DISPLAY,
|
||||
OPT_LIST_APPS,
|
||||
};
|
||||
|
||||
struct sc_option {
|
||||
@ -443,6 +444,11 @@ static const struct sc_option options[] = {
|
||||
"This is a workaround for some devices not behaving as "
|
||||
"expected when setting the device clipboard programmatically.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_LIST_APPS,
|
||||
.longopt = "list-apps",
|
||||
.text = "List Android apps installed on the device.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_LIST_CAMERAS,
|
||||
.longopt = "list-cameras",
|
||||
@ -2610,6 +2616,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
case OPT_LIST_CAMERA_SIZES:
|
||||
opts->list |= SC_OPTION_LIST_CAMERA_SIZES;
|
||||
break;
|
||||
case OPT_LIST_APPS:
|
||||
opts->list |= SC_OPTION_LIST_APPS;
|
||||
break;
|
||||
case OPT_REQUIRE_AUDIO:
|
||||
opts->require_audio = true;
|
||||
break;
|
||||
|
@ -304,6 +304,7 @@ struct scrcpy_options {
|
||||
#define SC_OPTION_LIST_DISPLAYS 0x2
|
||||
#define SC_OPTION_LIST_CAMERAS 0x4
|
||||
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
|
||||
#define SC_OPTION_LIST_APPS 0x10
|
||||
uint8_t list;
|
||||
bool window;
|
||||
bool mouse_hover;
|
||||
|
@ -371,6 +371,9 @@ execute_server(struct sc_server *server,
|
||||
if (params->list & SC_OPTION_LIST_CAMERA_SIZES) {
|
||||
ADD_PARAM("list_camera_sizes=true");
|
||||
}
|
||||
if (params->list & SC_OPTION_LIST_APPS) {
|
||||
ADD_PARAM("list_apps=true");
|
||||
}
|
||||
|
||||
#undef ADD_PARAM
|
||||
|
||||
|
@ -61,6 +61,7 @@ public class Options {
|
||||
private boolean listDisplays;
|
||||
private boolean listCameras;
|
||||
private boolean listCameraSizes;
|
||||
private boolean listApps;
|
||||
|
||||
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
|
||||
private boolean sendDeviceMeta = true; // send device name and size
|
||||
@ -213,7 +214,7 @@ public class Options {
|
||||
}
|
||||
|
||||
public boolean getList() {
|
||||
return listEncoders || listDisplays || listCameras || listCameraSizes;
|
||||
return listEncoders || listDisplays || listCameras || listCameraSizes || listApps;
|
||||
}
|
||||
|
||||
public boolean getListEncoders() {
|
||||
@ -232,6 +233,10 @@ public class Options {
|
||||
return listCameraSizes;
|
||||
}
|
||||
|
||||
public boolean getListApps() {
|
||||
return listApps;
|
||||
}
|
||||
|
||||
public boolean getSendDeviceMeta() {
|
||||
return sendDeviceMeta;
|
||||
}
|
||||
@ -395,6 +400,9 @@ public class Options {
|
||||
case "list_camera_sizes":
|
||||
options.listCameraSizes = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "list_apps":
|
||||
options.listApps = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "camera_id":
|
||||
if (!value.isEmpty()) {
|
||||
options.cameraId = value;
|
||||
|
@ -287,6 +287,11 @@ public final class Server {
|
||||
Workarounds.apply();
|
||||
Ln.i(LogUtils.buildCameraListMessage(options.getListCameraSizes()));
|
||||
}
|
||||
if (options.getListApps()) {
|
||||
Workarounds.apply();
|
||||
Ln.i("Processing Android apps... (this may take some time)");
|
||||
Ln.i(LogUtils.buildAppListMessage());
|
||||
}
|
||||
// Just print the requested data, do not mirror
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.genymobile.scrcpy.device;
|
||||
|
||||
import com.genymobile.scrcpy.AndroidVersions;
|
||||
import com.genymobile.scrcpy.FakeContext;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||
import com.genymobile.scrcpy.wrappers.DisplayControl;
|
||||
@ -9,6 +10,10 @@ import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
@ -17,6 +22,9 @@ import android.view.InputEvent;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class Device {
|
||||
|
||||
public static final int DISPLAY_ID_NONE = -1;
|
||||
@ -201,4 +209,37 @@ public final class Device {
|
||||
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
|
||||
return displayInfo.getRotation();
|
||||
}
|
||||
|
||||
public static List<DeviceApp> listApps() {
|
||||
List<DeviceApp> apps = new ArrayList<>();
|
||||
PackageManager pm = FakeContext.get().getPackageManager();
|
||||
for (ApplicationInfo appInfo : getLaunchableApps(pm)) {
|
||||
String name = pm.getApplicationLabel(appInfo).toString();
|
||||
boolean system = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
apps.add(new DeviceApp(appInfo.packageName, name, system, appInfo.enabled));
|
||||
}
|
||||
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
package com.genymobile.scrcpy.device;
|
||||
|
||||
public final class DeviceApp {
|
||||
|
||||
private final String packageName;
|
||||
private final String name;
|
||||
private final boolean system;
|
||||
private final boolean enabled;
|
||||
|
||||
public DeviceApp(String packageName, String name, boolean system, boolean enabled) {
|
||||
this.packageName = packageName;
|
||||
this.name = name;
|
||||
this.system = system;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSystem() {
|
||||
return system;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
package com.genymobile.scrcpy.util;
|
||||
|
||||
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.wrappers.DisplayManager;
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
@ -13,7 +16,9 @@ import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.media.MediaCodec;
|
||||
import android.util.Range;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@ -154,4 +159,55 @@ public final class LogUtils {
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
public static String buildAppListMessage() {
|
||||
StringBuilder builder = new StringBuilder("List of apps:");
|
||||
|
||||
List<DeviceApp> apps = Device.listApps();
|
||||
|
||||
// Sort by:
|
||||
// 1. system flag (system apps are before non-system apps)
|
||||
// 2. name
|
||||
// 3. package name
|
||||
Collections.sort(apps, (thisApp, otherApp) -> {
|
||||
// System apps first
|
||||
int cmp = -Boolean.compare(thisApp.isSystem(), otherApp.isSystem());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = Objects.compare(thisApp.getName(), otherApp.getName(), String::compareTo);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return Objects.compare(thisApp.getPackageName(), otherApp.getPackageName(), String::compareTo);
|
||||
});
|
||||
|
||||
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);
|
||||
if (padding > 0) {
|
||||
builder.append(String.format("%" + padding + "s", " "));
|
||||
} else {
|
||||
builder.append("\n ").append(String.format("%" + column + "s", " "));
|
||||
}
|
||||
builder.append(" [").append(app.getPackageName()).append(']');
|
||||
if (!app.isEnabled()) {
|
||||
builder.append(" (disabled)");
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user