Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get installed packages information #502

Merged
merged 16 commits into from Mar 9, 2023
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Expand Up @@ -14,6 +14,8 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />

<application android:allowBackup="true" tools:ignore="GoogleAppIndexingWarning">
<receiver android:name="io.appium.uiautomator2.server.ServerInstrumentation$PowerConnectionReceiver"
Expand Down
47 changes: 47 additions & 0 deletions app/src/main/java/io/appium/uiautomator2/handler/Getpackages.java
@@ -0,0 +1,47 @@
package io.appium.uiautomator2.handler;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved


import static io.appium.uiautomator2.model.Session.NO_ID;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
import static io.appium.uiautomator2.server.ServerInstrumentation.ServerContext;

import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import java.util.ArrayList;
import java.util.List;

import io.appium.uiautomator2.handler.request.NoSessionCommandHandler;
import io.appium.uiautomator2.handler.request.SafeRequestHandler;
import io.appium.uiautomator2.http.AppiumResponse;
import io.appium.uiautomator2.http.IHttpRequest;
import io.appium.uiautomator2.model.api.BatteryStatusModel;
import io.appium.uiautomator2.model.api.StatusModel;
import io.appium.uiautomator2.model.api.touch.PackageModel;
import io.appium.uiautomator2.server.ServerInstrumentation;
import io.appium.uiautomator2.utils.BatteryHelper;

public class Getpackages extends SafeRequestHandler implements NoSessionCommandHandler {
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
public Getpackages(String mappedUri) {
super(mappedUri);
}
public static List<PackageModel> appDetails = new ArrayList<PackageModel>();
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
@Override
protected AppiumResponse safeHandle(IHttpRequest request) {
try{
List<ApplicationInfo> apps = ServerContext.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
for(ApplicationInfo appInfo: apps) {
PackageModel model = new PackageModel();
if(ServerContext.getPackageManager().getLaunchIntentForPackage(appInfo.packageName)!=null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the package manager instance should be retrieved once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adb shell dumpsys activity activities and adb shell dumpsys package <packagename> as well? Perhaps they will give more information though.

I'm curious about what information could get only via this

Well first command will only give information of the activities information of the current opened application, and also the main issue is reading the dumpsys is another headache, maybe we can achieve but this seems the more efficient way of doing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. So, this pr aims to show a few information which could be part of adb shell dumpsys package <packagename> via UIA2 server process instead of adb command, correct? (to clarify the intention)
package name, an activity name which is specified as launcher category and the package name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly, So is packageName and activityName correct? or should i rename to any other name for better understanding?

model.setPackageName(appInfo.packageName);
model.setAppName((String) ServerContext.getPackageManager().getApplicationLabel(appInfo));
model.setPackageActivity(ServerContext.getPackageManager().getLaunchIntentForPackage(appInfo.packageName).getComponent().getClassName());
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved
appDetails.add(model);
}
}
}catch (Exception e){
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
e.printStackTrace();
}
return new AppiumResponse(NO_ID, appDetails);
}
}
@@ -0,0 +1,31 @@
package io.appium.uiautomator2.model.api.touch;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved

public class PackageModel {
String PackageName;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
String PackageActivity;
String AppName;

public String getAppName() {
return AppName;
}

public String getPackageName() {
shashank2086 marked this conversation as resolved.
Show resolved Hide resolved
return PackageName;
}

public void setPackageName(String packageName) {
PackageName = packageName;
}

public void setAppName(String appName) {
AppName = appName;
}

public String getPackageActivity() {
return PackageActivity;
}

public void setPackageActivity(String packageActivity) {
PackageActivity = packageActivity;
}
}
Expand Up @@ -56,6 +56,7 @@
import io.appium.uiautomator2.handler.GetSize;
import io.appium.uiautomator2.handler.GetSystemBars;
import io.appium.uiautomator2.handler.GetText;
import io.appium.uiautomator2.handler.Getpackages;
import io.appium.uiautomator2.handler.Location;
import io.appium.uiautomator2.handler.LongPressKeyCode;
import io.appium.uiautomator2.handler.MultiPointerGesture;
Expand Down Expand Up @@ -161,6 +162,7 @@ private void registerPostHandler() {

private void registerGetHandler() {
register(getHandler, new Status("/status"));
register(getHandler,new Getpackages("/apps"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a standard w3c endpoint, so we should not expose it like this. Consider adding this data into a custom /appium/device/... endpoint instead. Also, do you know how it is going to be called from the driver?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For driver, we have to add in the driver Wrapper SDK right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the endpoint to /appium/device/apps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to have this endpoint without a session id?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For driver, we have to add in the driver Wrapper SDK right?

There should be a driver endpoint, which would expose this information. You could implement it in two ways, either using a mobile: ... extension or by adding a custom API endpoint and assume that clients would add a support for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No they can utilize the API with driver also right, see i need it before creating session but it's not necessary for everyone, they can also utilize it after creating a session like every other APIs

Copy link
Member

@KazuCocoa KazuCocoa Mar 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok to add a new endpoint, if not a complicated feature to keep maintaining, but I'd request to add test code to check the function (in this repo, or via uia2 driver). Endpoints which are not in regular WebDriver protocol and not used by UIA2 driver regularly could break because we don't care so much for as our project. (Actually, fork is the most flexible way to maintain your own way as Mykola addressed.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well i can maintain it, but i think it's a quite a useful feature for the End User to have and i think it can be easily maintained and should not affect the existing implementation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have already commented appium/appium-uiautomator2-driver#585
It is just not going to work without a session id.

If you really need to have the endpoint in this rep I can propose to create two handlers for it - one with session id and one without (session/:sessionId/appium/device/apps and /appium/device/apps ). The latter could be then used in custom calls, but since the code would be shared, we can guarantee it works as expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay makes sense, i will do that

register(getHandler, new GetSessions("/sessions"));
register(getHandler, new GetSessionDetails("/session/:sessionId"));
register(getHandler, new CaptureScreenshot("/session/:sessionId/screenshot"));
Expand Down
Expand Up @@ -40,12 +40,13 @@
import static io.appium.uiautomator2.server.ServerConfig.getServerPort;
import static io.appium.uiautomator2.utils.Device.getUiDevice;


public class ServerInstrumentation {
private static final int MIN_PORT = 1024;
private static final int MAX_PORT = 65535;
private static final String WAKE_LOCK_TAG = "UiAutomator2:ScreenKeeper";
public static final long MAX_TEST_DURATION = 24 * 60 * 60 * 1000;

public static Context ServerContext = null;
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
private static ServerInstrumentation instance;

private final PowerManager powerManager;
Expand Down Expand Up @@ -76,6 +77,7 @@ private void setAccessibilityServiceState() {
}

private ServerInstrumentation(Context context, int serverPort, int mjpegServerPort) {
ServerContext=context;
if (isValidPort(serverPort)) {
this.serverPort = serverPort;
} else {
Expand Down