Android:以编程方式打开API 24+中的Overview / Obnts屏幕

时间:2017-01-05 19:14:14

标签: android

我正在尝试在Android中打开Overview / Recents屏幕。从Android: Programmatically open "Recent Apps" dialog开始,我可以使用以下代码:

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);
}

但是,自从Nougat以来toggleRecentApps()no longer in IStatusBarService,它会导致NoSuchMethodException。

是否有其他方法(不包括AccessibilityService)可用于打开概述/最近的屏幕?

编辑:实现IStatusBarService的类似乎是StatusBarManagerService。但是,调用statusBarObject.getClass().getCanonicalName()会返回com.android.internal.statusbar.IStatusBarService.Stub.Proxy,因此通过私有字段IStatusBar获取toggleRecentApps()(确实实现mBar)似乎不起作用获得它的方式。

编辑2 :看到StatusBarManagerInternal.java是一个包含void toggleRecentApps()的界面,我试图找到它的实现,我在StatusBarManagerService中找到了它:

private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { ... }

所以它是StatusManagerService中的匿名类。 >:(

然而,我也发现:

/**
 * 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);
}

所以它显然在LocalServices中注册了它,而后者又在ArrayMap中管理它:

private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
        new ArrayMap<Class<?>, Object>();

因此,我试图访问它:

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);
}

然而:

/**
 * 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}
 */

唉,我必须在SystemUI过程中(或者看起来似乎)能够访问它。 (实际上,ArrayMap为空,导致我的尝试无用。)

任何关于如何从这里开始的指导将不胜感激。

1 个答案:

答案 0 :(得分:0)

创建从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)
    }
}

致电您需要采取行动的doAction()

添加到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" />

了解更多信息,请查看https://developer.android.com/guide/topics/ui/accessibility/services.html