I am trying to open the Overview/Recents screen in Android. From Android: Programmatically open "Recent Apps" dialog, I can use the following code:
try {
Class serviceManagerClass = Class.forName("android.os.ServiceManager");
Method getService = serviceManagerClass.getMethod("getService", String.class);
IBinder retbinder = (IBinder) getService.invoke(null, "statusbar");
Class statusBarClass = Class.forName(retbinder.getInterfaceDescriptor());
Object statusBarObject = statusBarClass.getClasses()[0]
.getMethod("asInterface", IBinder.class).invoke(null, retbinder);
Method toggleRecentApps = statusBarClass.getMethod("toggleRecentApps");
toggleRecentApps.setAccessible(true);
toggleRecentApps.invoke(statusBarObject);
} catch (InvocationTargetException| NoSuchMethodException | RemoteException | IllegalAccessException | ClassNotFoundException e){
handleError(e);
}
However, toggleRecentApps()
is no longer in IStatusBarService since Nougat, and it causes a NoSuchMethodException.
Is there any other way (excluding AccessibilityService) that can be used to open the Overview/Recents screen?
EDIT: The class that implements IStatusBarService seems to be StatusBarManagerService. However, calling statusBarObject.getClass().getCanonicalName()
returns com.android.internal.statusbar.IStatusBarService.Stub.Proxy
, so getting an IStatusBar
(which does implement toggleRecentApps()
) through the private field mBar
doesn't seem to be a working way to get it.
EDIT 2: Seeing that StatusBarManagerInternal.java is an interface that contains void toggleRecentApps()
, I tried to find its implementation, which I found in StatusBarManagerService
:
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { ... }
So it's an anonymous class in StatusManagerService. >:(.
However, I also found:
/**
* Construct the service, add the status bar view to the window manager
*/
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
}
So it apparently registers it in LocalServices
, which in turn manages it in an ArrayMap
:
private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
new ArrayMap<Class<?>, Object>();
Therefore, I tried to access it:
try {
Field services = Class.forName("com.android.server.LocalServices")
.getDeclaredField("sLocalServiceObjects");
services.setAccessible(true);
ArrayMap<Class<?>, Object> serviceMap = (ArrayMap<Class<?>, Object>) services.get(null);
Set<Map.Entry<Class<?>, Object>> serviceSet = serviceMap.entrySet();
for (Map.Entry<Class<?>, Object> serviceEntry : serviceSet) {
if (serviceEntry.getKey().isInterface()) {
if ("com.android.server.statusbar.StatusBarManagerInternal"
.equals(serviceEntry.getKey().getName())) {
Object statusBarInternalObject = serviceEntry.getValue();
Class statusBarInternalClass = serviceEntry.getKey();
Method openRecents = statusBarInternalClass.getMethod("toggleRecentApps");
openRecents.setAccessible(true);
openRecents.invoke(statusBarInternalObject);
return;
}
}
}
} catch (Exception e) {
handleError(e);
}
However:
/**
* This class is used in a similar way as ServiceManager, except the services registered here
* are not Binder objects and are only available in the same process.
*
* Once all services are converted to the SystemService interface, this class can be absorbed
* into SystemServiceManager.
*
* {@hide}
*/
Alas, I have to be in the SystemUI process (or so it seems) to be able to access it. (Effectively, the ArrayMap
is empty, rendering my attempt useless.)
Any guidance on how to proceed from here would be appreciated.
One option a lot of developers overlook is the Overview Screen. As the developer docs state: The overview screen (also referred to as the recents screen, recent task list, or recent apps) is a system-level UI that lists recently accessed activities and tasks.
The Recents key — the square you see in the bottom-right corner of most Android phones — is a staple of the OS, letting you see a card-deck of recently-used apps.
The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack.
Create service extended from AccessibilityService
:
class ExampleAccessService:AccessibilityService() {
override fun onInterrupt() {
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
}
fun doAction(){
performGlobalAction(GLOBAL_ACTION_RECENTS)
// performGlobalAction(GLOBAL_ACTION_BACK)
// performGlobalAction(GLOBAL_ACTION_HOME)
// performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS)
// performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)
// performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS)
// performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)
}
}
Call doAction()
where you want action
Add to Manifest
:
<application
...
<service
android:name=".ExampleAccessService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:label="Name of servise" // it will be viewed in Settings->Accessibility->Services
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config"/>
</service>
...
</application>
accessibility_service_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="false"
android:description="your description"
android:notificationTimeout="100"
android:packageNames="your app package, ex: ex: com.example.android"
android:settingsActivity="your settings activity ex: com.example.android.MainActivity" />
for more info look at https://developer.android.com/guide/topics/ui/accessibility/services.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With