繁体   English   中英

Android:使用API​​ 24+以编程方式打开“概述/最新”屏幕

[英]Android: programmatically open Overview/Recents screen in API 24+

我正在尝试在Android中打开“概述/最新信息”屏幕。 Android:以编程方式打开“最近使用的应用程序”对话框 ,我可以使用以下代码:

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

但是,自从牛轧糖以来, toggleRecentApps()就不再存在 toggleRecentApps() ,它会导致NoSuchMethodException。

是否可以使用其他任何方式(不包括AccessibilityService)打开“概述/近期”屏幕?


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


编辑2 :看到StatusBarManagerInternal.java是一个包含void toggleRecentApps()的接口,我试图找到其实现,该StatusBarManagerServiceStatusBarManagerService找到:

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注册了它,而LocalServices又在ArrayMap对其进行了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}
 */

las,我必须在SystemUI进程中(或看来)才能访问它。 (实际上, ArrayMap为空,使我的尝试无用。)

任何有关如何从此处进行的指导将不胜感激。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM