【问题标题】:Android: programmatically open Overview/Recents screen in API 24+Android:在 API 24+ 中以编程方式打开概览/最近屏幕
【发布时间】:2018-02-02 00:06:33
【问题描述】:

我正在尝试在 Android 中打开“概览/最近”屏幕。从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);
}

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

还有其他方式(不包括 AccessibilityService)可以用来打开概览/最近的屏幕吗?


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


编辑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 是空的,使我的尝试毫无用处。)

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

【问题讨论】:

  • 这是通过 API 可以更改的反射调用的方法的问题。
  • 该方法也不一定在任何给定设备上都可用,因为欢迎设备制造商和自定义 ROM 开发人员根据他们认为合适的方式修改 Android 内部结构。
  • @CommonsWare 是的,但我至少需要它才能在普通 Android 上工作。
  • 好的,刚刚再次尝试,但失败了。我可以访问持有StatusBarInternal 的同一张地图吗?或者以某种方式访问​​该StatusBarInternal 实例?或者至少是IStatusBar

标签: android


【解决方案1】:

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

【讨论】:

  • 简而言之:我已经尝试了解决方案,但无法执行操作,可能缺少一些初始化。
  • 您是否开启了设备上的辅助功能服务?设置 -> 辅助功能 -> 服务 -> 您的服务名称(在屏幕顶部)-> 将其打开
  • public class ExampleAccessService extends AccessibilityService {private static ExampleAccessService instance;@Override protected void onServiceConnected(){super.onServiceConnected();instance=this;}@Override public boolean onUnbind(Intent intent){instance= null;return super.onUnbind(intent);}@Override public void onInterrupt(){}@Override public void onAccessibilityEvent(AccessibilityEvent event){} public static ExampleAccessService getInstance(){return instance;}@RequiresApi(api=Build.VERSION_CODES .JELLY_BEAN) public void openRecents(){performGlobalAction(GLOBAL_ACTION_RECENTS);}}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 2017-01-29
  • 2015-01-28
  • 1970-01-01
相关资源
最近更新 更多