android16单手模式代码走读

Android 16 单手模式实现原理与代码走读

适用版本 :AOSP android-16.0.0_r4

单手模式,在功能层面,就是把整个屏幕画面移动到一个合适的位置,方便单手控制。

技术方面,是通过SystemUi注册了针对FEATURE_ONE_HANDED的DisplayAreaOrganizer,通过此organizer来控制FEATURE_ONE_HANDED对应DisplayArea的Surface位置和大小。

SystemUi透出一个binder对象,并提供startOneHanded和stopOneHanded接口来开始和结束单手模式。

1. 概述

Android 16 的单手模式代码的核心效果是:

  1. SystemUI/WMShell 初始化 OneHandedController
  2. OneHandedDisplayAreaOrganizer 注册 FEATURE_ONE_HANDED,获得该 feature 对应的一个或多个 DisplayArea leash。
  3. 进入单手模式时,根据显示高度和 config_one_handed_offset 计算纵向偏移,默认配置为显示高度的 40%。
  4. 在 600 ms 动画过程中,对每个 leash 持续执行 setPosition(0, offset),同时设置 window crop 和圆角。
  5. 退出时把纵向偏移从当前值动画恢复到 0。

通过平移 FEATURE_ONE_HANDED 覆盖范围内的 Surface 层级,并配合裁剪和圆角,形成屏幕内容整体下移的单手操作区域 。代码中没有对 leash 调用 setMatrix() 或缩放 API,所以"缩小整个画面"并不准确。

2. 实现要点

2.1 画面处理方式

单手模式通过 setPosition(leash, 0, offset) 将目标 Surface 层级沿 Y 轴向下移动,并结合 setWindowCrop()setCornerRadius() 设置裁剪范围和圆角。该实现未使用 setMatrix() 等缩放接口,因此核心机制是平移、裁剪和圆角处理

2.2 DisplayArea 与 leash

registerOrganizer(FEATURE_ONE_HANDED) 返回 List<DisplayAreaAppearedInfo>。Organizer 会逐项处理并保存对应的 token 和 leash。DisplayAreaPolicy 可以根据窗口 layer range 和 feature 嵌套关系形成多个带有 OneHanded feature 的 DisplayArea 节点,进入或退出单手模式时会遍历这些节点并同步执行动画。

2.3 Binder 接口传递

IOneHanded 的接口实现位于 WM Shell。OneHandedController 通过 ShellController.addExternalInterface() 注册外部接口;Shell 初始化阶段将 Binder 写入 Bundle;SystemUI 负责绑定 Quickstep 服务并转交初始化数据;Launcher/Quickstep 从 Bundle 中取得 IOneHanded,随后调用 startOneHanded()stopOneHanded()

2.4 Car Launcher 场景

CarQuickStepService 在 manifest 中声明 android.intent.action.QUICKSTEP_SERVICE,可以作为车载产品中的 Quickstep 服务组件。实际使用的服务由产品配置、overlay、默认 Launcher 以及组件解析结果决定。

2.5 DisplayArea 创建与 Organizer 注册

FEATURE_ONE_HANDED 对应的 DisplayArea 层级通常在 WindowManager 构建 DisplayAreaPolicy 时创建。registerOrganizer() 的主要职责是注册 organizer,并获取已经出现的 DisplayArea token/leash。后续新增显示或层级变化时,Organizer 还可以通过 onDisplayAreaAppeared() 接收新的节点。

3. 推荐阅读顺序

为便于理解,建议按下面顺序阅读文末完整代码记录:

  1. 配置项:偏移比例、动画时长、圆角开关。
  2. WMShell 初始化WMShell.start() 调用 initOneHanded()
  3. OneHandedController 初始化 :注册配置、显示、用户、无障碍监听,并发布 IOneHanded 外部接口。
  4. Organizer 启停updateOneHandedEnabled() 注册或注销 FEATURE_ONE_HANDED
  5. DisplayArea 收集registerOrganizer() 获取并保存 token/leash。
  6. Binder 传递ShellController 创建 Binder,SystemUI 经 Quickstep 初始化 Bundle 传给 Launcher。
  7. 进入单手模式startOneHanded() 检查状态、方向和 readiness,计算 Y 偏移。
  8. 动画调度scheduleOffset() 遍历所有 leash,创建 animator。
  9. Surface 提交:动画帧内执行 crop、round、translate。
  10. 窗口层级验证 :结合 dumpsys window containers 判断哪些窗口属于 OneHanded feature。

4. 关键调用链

text 复制代码
WMShell.start()
  -> initOneHanded(oneHanded)

OneHandedController.onInit()
  -> addExternalInterface(IOneHanded.DESCRIPTOR, ...)
  -> updateSettings()
  -> updateOneHandedEnabled()
       -> registerOrganizer(FEATURE_ONE_HANDED)

外部触发:IOneHanded.startOneHanded()
  -> IOneHandedImpl.startOneHanded()
  -> executeRemoteCallWithTaskPermission(...)
  -> OneHandedController.startOneHanded()
       -> yOffset = displayHeight * offsetFraction
       -> OneHandedDisplayAreaOrganizer.scheduleOffset(0, yOffset)
            -> forEach(token, leash)
            -> animateWindows(...)
            -> ValueAnimator.start()
                 -> onAnimationUpdate()
                 -> crop + round + translate
                 -> Transaction.apply()

5. 对 dumpsys 层级的解读

从输入样例可见,OneHanded 并非单一连续节点,而是在不同层级范围中多次出现。需要关注以下几点:

  • DefaultTaskDisplayArea 位于 OneHanded:0:14 下,因此普通应用、Home、壁纸等低层级内容会受这一路 OneHanded leash 控制。
  • StatusBar 位于独立的 OneHanded:15:15 下,意味着它也有对应的 OneHanded feature 节点,但是否视觉上同步位移要以 organizer 实际取得的 leash 和产品配置为准。
  • NotificationShade 位于 OneHanded:17:17 下,同样被单独切分。
  • Taskbar、手势处理窗口和 ScreenDecorOverlay 所在层级不一定都处于 OneHanded 节点内,因此不能笼统地说"所有系统窗口都会一起移动"。
  • WindowedMagnificationHideDisplayCutoutOneHandedAppZoomOut 的嵌套顺序反映 DisplayArea feature 的构建顺序及层级覆盖关系。某个 layer 最终由包含它的整条父子 Surface 链共同影响,而不是只由名称最接近的 feature 单独控制。

6. 准确性确认

本次整理按以下证据进行了内部一致性校验:

  • startOneHanded() 的偏移计算与 config_one_handed_offset=40% 一致。
  • animator 的 setDuration()config_one_handed_translate_animation_duration=600 一致。
  • scheduleOffset() 遍历 mDisplayAreaTokenMap,与多个 DisplayAreaAppearedInfo 的处理方式一致。
  • 每帧实际调用 crop、round、translate,支持"平移并裁剪",不支持"按比例缩小"的说法。
  • IOneHandedImpl 通过 executeRemoteCallWithTaskPermission() 转发,说明外部 Binder 调用还包含任务级权限检查和 Shell executor 线程切换语义。

注意:材料未包含 DisplayAreaPolicy 的 feature builder 配置、initOneHanded() 完整实现、ShellInterfaceLauncherProxyService 的完整初始化调用点,以及 stopOneHanded() 主体。因此本文对这些部分只给出由现有代码可支持的链路说明,不虚构缺失实现。


附录 A:完整代码摘要与输出记录

以下内容保留输入文件中的全部代码示例和 dumpsys 输出,仅做 Markdown 转义清理。原始注释予以保留;正文中的实现说明采用经过校准后的准确表述。

单手模式,在功能层面,就是把整个屏幕画面缩小到一个较小的尺寸,方便单手控制。

技术方面,是通过SystemUi注册了针对FEATURE_ONE_HANDED的DisplayAreaOrganizer,通过此organizer来控制FEATURE_ONE_HANDED对应DisplayArea的Surface位置和大小。

SystemUi透出一个binder对象,并提供startOneHanded和stopOneHanded接口来开始和结束单手模式。

android-16.0.0_r4/frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java

@Override

public void start() {

// Notify with the initial configuration and subscribe for new config changes

mShell.onConfigurationChanged(mContext.getResources().getConfiguration());

mConfigurationController.addCallback(mConfigurationListener);

复制代码
    // Subscribe to keyguard changes
    mKeyguardStateController.addCallback(mKeyguardStateCallback);
    mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);

    // Subscribe to user changes
    mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
    mUserChangedCallback.onUserChanged(mUserTracker.getUserId(),
            mContext.createContextAsUser(mUserTracker.getUserHandle(), 0 /* flags */));
    mUserChangedCallback.onProfilesChanged(mUserTracker.getUserProfiles());

    mCommandQueue.addCallback(this);
    mCommandRegistry.registerCommand("wmshell-passthrough", () -> mShellCommand);

    mPipOptional.ifPresent(this::initPip);
    mSplitScreenOptional.ifPresent(this::initSplitScreen);
    mOneHandedOptional.ifPresent(this::initOneHanded); ////////////////////////////////////////
    mDesktopModeOptional.ifPresent(this::initDesktopMode);
    mRecentTasksOptional.ifPresent(this::initRecentTasks);

    mNoteTaskInitializer.initialize();
}

android-16.0.0_r4/frameworks/base/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java

复制代码
    oneHanded.registerEventCallback(new OneHandedEventCallback() {
        @Override
        public void notifyExpandNotification() {
            mSysUiMainExecutor.execute(
                    () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
        }
    });

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java

复制代码
private void updateOneHandedEnabled() {
    if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
        mMainExecutor.execute(() -> stopOneHanded());
    }

    // If setting is pull screen, notify shortcut one_handed_mode_activated to reset
    // and align status with current mState when one-handed gesture enabled.
    if (isOneHandedEnabled() && !isSwipeToNotificationEnabled()) {
        notifyShortcutStateChanged(mState.getState());
    }

    mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);

    if (!mIsOneHandedEnabled) {
        mDisplayAreaOrganizer.unregisterOrganizer();
        // Do NOT register + unRegister DA in the same call
        return;
    }

    if (mDisplayAreaOrganizer.getDisplayAreaTokenMap().isEmpty()) {
        mDisplayAreaOrganizer.registerOrganizer(
                OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED); /////////////////////注册并获取对应的DisplayAreaAppearedInfo列表。
    }
}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java

@Override

public List registerOrganizer(int displayAreaFeature) {

final List displayAreaInfos =

super.registerOrganizer(displayAreaFeature); /////////////////////////////////// 存储FEATURE_ONE_HANDED对应的DisplayAreaAppearedInfo列表,一个display一般有多个DisplayAreaAppearedInfo。

for (int i = 0; i < displayAreaInfos.size(); i++) {

final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);

onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());

}

mIsReady = true;

updateDisplayBounds();

return displayAreaInfos;

}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java

@Override

public void onAnimationUpdate(ValueAnimator animation) {

final SurfaceControl.Transaction tx = newSurfaceControlTransaction();

mOneHandedAnimationCallbacks.forEach(

(callback) -> callback.onAnimationUpdate(tx, 0f, mCurrentValue)

);

applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());

}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java

private static class IOneHandedImpl extends IOneHanded.Stub implements ExternalInterfaceBinder { ///////////////////

private OneHandedController mController;

复制代码
private void onInit() {
    mShellCommandHandler.addDumpCallback(this::dump, this);
    mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
    mDisplayController.addDisplayChangingController(this);
    setupCallback();
    registerSettingObservers(mUserId);
    setupTimeoutListener();
    updateSettings();
    updateDisplayLayout(mContext.getDisplayId());

    mAccessibilityManager.addAccessibilityStateChangeListener(
            mAccessibilityStateChangeListener);

    mState.addSListeners(mTutorialHandler);
    mShellController.addConfigurationChangeListener(this);
    mShellController.addKeyguardChangeListener(this);
    mShellController.addUserChangeListener(this);
    mShellController.addExternalInterface(IOneHanded.DESCRIPTOR,
            this::createExternalInterface, this);////////////////////////////////
}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java

@VisibleForTesting

public void createExternalInterfaces(Bundle output) {

// Invalidate the old binders

for (int i = 0; i < mExternalInterfaces.size(); i++) {

final ExternalInterfaceBinder extInterface = mExternalInterfaces.valueAt(i);

ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "Invalidating external interface: %s",

extInterface.getClass().getSimpleName());

extInterface.invalidate();

}

mExternalInterfaces.clear();

复制代码
    // Create new binders for each key
    for (int i = 0; i < mExternalInterfaceSuppliers.size(); i++) {
        final String key = mExternalInterfaceSuppliers.keyAt(i);
        final ExternalInterfaceBinder b = mExternalInterfaceSuppliers.valueAt(i).get();
        mExternalInterfaces.put(key, b);
        output.putBinder(key, b.asBinder());
    }
}

android-16.0.0_r4/packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java

public static class TISBinder extends ILauncherProxy.Stub {

复制代码
    private final WeakReference<TouchInteractionService> mTis;

    private TISBinder(TouchInteractionService tis) {
        mTis = new WeakReference<>(tis);
    }

    @BinderThread
    public void onInitialize(Bundle bundle) {
        ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
                bundle.getBinder(ISystemUiProxy.DESCRIPTOR));
        IPip pip = IPip.Stub.asInterface(bundle.getBinder(IPip.DESCRIPTOR));
        IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(IBubbles.DESCRIPTOR));
        ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
                ISplitScreen.DESCRIPTOR));
        IOneHanded onehanded = IOneHanded.Stub.asInterface(
                bundle.getBinder(IOneHanded.DESCRIPTOR));

// 通过LauncherProxyService绑定ACTION_QUICKSTEP,并把onehand binder接口透出给launcher。

android-16.0.0_r4/frameworks/base/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java

public class LauncherProxyService implements CallbackController,

NavigationModeController.ModeChangedListener, Dumpable {

复制代码
@VisibleForTesting
static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";

android-16.0.0_r4/frameworks/base/packages/SystemUI/src/com/android/systemui/LauncherProxyService.java

private final ServiceConnection mLauncherServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

Log.d(TAG_OPS, "Launcher proxy service connected");

mConnectionBackoffAttempts = 0;

mHandler.removeCallbacks(mDeferredConnectionCallback);

try {

service.linkToDeath(mLauncherServiceDeathRcpt, 0);

} catch (RemoteException e) {

// Failed to link to death (process may have died between binding and connecting),

// just unbind the service for now and retry again

Log.e(TAG_OPS, "Lost connection to launcher service", e);

disconnectFromLauncherService("Lost connection to launcher service");

retryConnectionWithBackoff();

return;

}

复制代码
        mCurrentBoundedUserId = mUserTracker.getUserId();
        mLauncherProxy = ILauncherProxy.Stub.asInterface(service);

SystemUI 绑定这个服务,透出IOneHanded接口来控制startOneHanded和stopOneHanded

android-16.0.0_r4/packages/apps/Car/Launcher/app/AndroidManifest.xml

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl

interface IOneHanded {

复制代码
/**
 * Enters one handed mode.
 */
oneway void startOneHanded() = 1;

/**
 * Exits one handed mode.
 */
oneway void stopOneHanded() = 2;

}

#########################

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java

复制代码
private ExternalInterfaceBinder createExternalInterface() {
    return new IOneHandedImpl(this);
}

    private OneHandedController mController;

    IOneHandedImpl(OneHandedController controller) {
        mController = controller;
    }


    @Override
    public void startOneHanded() {
        executeRemoteCallWithTaskPermission(mController, "startOneHanded",
                (controller) -> {
                    controller.startOneHanded(); /////////////////////////////////////
                });
    }

    @Override
    public void stopOneHanded() {
        executeRemoteCallWithTaskPermission(mController, "stopOneHanded",
                (controller) -> {
                    controller.stopOneHanded(); ////////////////////////////////////////
                });
    }

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/res/values/config.xml

600

复制代码
<!-- One handed mode default offset % of display size -->
<fraction name="config_one_handed_offset">40%</fraction>

<!-- Allow one handed to enable round corner -->
<bool name="config_one_handed_enable_round_corner">true</bool>

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java

@VisibleForTesting

void startOneHanded() {

if (isLockedDisabled() || mKeyguardShowing) {

Slog.d(TAG, "Temporary lock disabled");

return;

}

复制代码
    if (!mDisplayAreaOrganizer.isReady()) {
        // Must wait until DisplayAreaOrganizer is ready for transitioning.
        mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS);
        return;
    }

    if (mState.isTransitioning() || mState.isInOneHanded()) {
        return;
    }

    if (mDisplayAreaOrganizer.getDisplayLayout().isLandscape()) {
        Slog.w(TAG, "One handed mode only support portrait mode");
        return;
    }

    mState.setState(STATE_ENTERING);
    final int yOffSet = Math.round(
            mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
    mOneHandedAccessibilityUtil.announcementForScreenReader(
            mOneHandedAccessibilityUtil.getOneHandedStartDescription());
    mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); ////////////////////////////////////// 0 -> 40%
    mTimeoutHandler.resetTimer();
    mOneHandedUiEventLogger.writeEvent(
            OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java

public void scheduleOffset(int xOffset, int yOffset) {

final float fromPos = mLastVisualOffset;

final int direction = yOffset > 0

? TRANSITION_DIRECTION_TRIGGER

: TRANSITION_DIRECTION_EXIT;

if (direction == TRANSITION_DIRECTION_TRIGGER) {

beginCUJTracing(CUJ_ONE_HANDED_ENTER_TRANSITION, "enterOneHanded");

} else {

beginCUJTracing(CUJ_ONE_HANDED_EXIT_TRANSITION, "stopOneHanded");

}

mDisplayAreaTokenMap.forEach(

(token, leash) -> {

animateWindows(token, leash, fromPos, yOffset, direction,

mEnterExitAnimationDurationMs); /////////////// 针对FEATURE_ONE_HANDED对应的DisplayAreaAppearedInfo进行调整。

});

mLastVisualOffset = yOffset;

}

复制代码
private void animateWindows(WindowContainerToken token, SurfaceControl leash, float fromPos,
        float toPos, @OneHandedAnimationController.TransitionDirection int direction,
        int durationMs) {
    final OneHandedAnimationController.OneHandedTransitionAnimator animator =
            mAnimationController.getAnimator(token, leash, fromPos, toPos,
                    mLastVisualDisplayBounds);
    if (animator != null) {
        animator.setTransitionDirection(direction)
                .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
                .addOneHandedAnimationCallback(mTutorialHandler)
                .setDuration(durationMs) ////////////////////////////////动画时长600ms
                .start();
    }
}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java

@Override

public void onAnimationUpdate(ValueAnimator animation) {

final SurfaceControl.Transaction tx = newSurfaceControlTransaction();

mOneHandedAnimationCallbacks.forEach(

(callback) -> callback.onAnimationUpdate(tx, 0f, mCurrentValue)

);

applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction()); ///////////////////

}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java

@Override

void applySurfaceControlTransaction(SurfaceControl leash,

SurfaceControl.Transaction tx, float fraction) {

final float start = getStartValue();

final float end = getEndValue();

final float currentValue = getCastedFractionValue(start, end, fraction);

mTmpRect.set(

mTmpRect.left,

mTmpRect.top + Math.round(currentValue),

mTmpRect.right,

mTmpRect.bottom + Math.round(currentValue));

setCurrentValue(currentValue);

getSurfaceTransactionHelper()

.crop(tx, leash, mTmpRect) // 调整window crop

.round(tx, leash) // 圆角

.translate(tx, leash, currentValue); // 调整surface位置

tx.apply();

}

android-16.0.0_r4/frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java

OneHandedSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,

Rect destinationBounds) {

tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());

return this;

}

复制代码
OneHandedSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash) {
    if (mEnableCornerRadius) {
        tx.setCornerRadius(leash, mCornerRadius);
    }
    return this;
}

OneHandedSurfaceTransactionHelper translate(SurfaceControl.Transaction tx, SurfaceControl leash,
        float offset) {
    tx.setPosition(leash, 0, offset);
    return this;
}

~/disk2/temp$ adb shell dumpsys window containers

└─ ROOT type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ Display 0 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=0,01080,2424 bounds=0,01080,2424

├─ Leaf:36:36 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ WindowToken{2dc7be7 type=2024 android.os.BinderProxy@f2095a6} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ 14b5394 ScreenDecorOverlayBottom type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ WindowToken{8c0ea5 type=2024 android.os.BinderProxy@67e49c} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ c875a2b ScreenDecorOverlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ HideDisplayCutout:32:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ OneHanded:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ AppZoomOut:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ Leaf:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ AppZoomOut:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ Leaf:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ OneHanded:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ AppZoomOut:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ WindowedMagnification:0:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ HideDisplayCutout:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ OneHanded:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ AppZoomOut:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ Leaf:24:25 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ WindowToken{d9c2a5a type=2024 android.os.BinderProxy@cfefa05} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ c01a48b EdgeBackGestureHandler0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ WindowToken{4be48f8 type=2019 android.os.BinderProxy@5def2de} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ ce69de0 Taskbar type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ HideDisplayCutout:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ OneHanded:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ AppZoomOut:23:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ Leaf:23:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Leaf:22:22 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ AppZoomOut:20:21 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ Leaf:20:21 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Leaf:19:19 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ AppZoomOut:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ OneHanded:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ WindowToken{ce9ec25 type=2040 android.os.BinderProxy@884d8f} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ f2b01fa NotificationShade type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ HideDisplayCutout:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ OneHanded:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ AppZoomOut:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ OneHanded:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Leaf:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ WindowToken{2ea7add type=2000 android.os.BinderProxy@61743c6} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ 6f05220 StatusBar type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ HideDisplayCutout:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ OneHanded:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

├─ AppZoomOut:2:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ ImePlaceholder:13:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ ImeContainer type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ WindowToken{e33e6b4 type=2011 android.os.Binder@f66ebc6} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ df0e6f InputMethod type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Leaf:3:12 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ WindowToken{f5ecccb type=2038 android.os.BinderProxy@65e5e1a} type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ d34f48e ShellDropTarget type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Task=10 type=standard mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ ActivityRecord{100219409 u0 com.android.settings/.Settings t10} type=standard mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ 5ae2c4b com.android.settings/com.android.settings.Settings type=standard mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Task=1 type=HOME mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ Task=2 type=HOME mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ ├─ ActivityRecord{88628082 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t2} type=HOME mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ │ └─ 203614d com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity type=HOME mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ │ └─ TaskFragment{36abc49 mode=multi-window} type=HOME mode=MULTI-WINDOW override-mode=MULTI-WINDOW requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ Task=3 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=0,00,0 bounds=0,01080,2424

│ ├─ Task=5 type=undefined mode=MULTI-WINDOW override-mode=MULTI-WINDOW requested-bounds=0,24241080,3636 bounds=0,24241080,3636

│ └─ Task=4 type=undefined mode=MULTI-WINDOW override-mode=MULTI-WINDOW requested-bounds=0,00,0 bounds=0,01080,2424

├─ Leaf:1:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ WallpaperWindowToken{4d5dde3 showWhenLocked=true} type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=0,00,0 bounds=0,01080,2424

│ └─ 442b22 com.android.systemui.wallpapers.ImageWallpaper type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ AppZoomOut:0:0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

└─ Leaf:0:0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=0,00,0 bounds=0,01080,2424

Window{14b5394 u0 ScreenDecorOverlayBottom}

Window{c875a2b u0 ScreenDecorOverlay}

Window{c01a48b u0 EdgeBackGestureHandler0}

Window{ce69de0 u0 Taskbar}

Window{f2b01fa u0 NotificationShade}

Window{6f05220 u0 StatusBar}

Window{d34f48e u0 ShellDropTarget}

Window{df0e6f u0 InputMethod}

Window{5ae2c4b u0 com.android.settings/com.android.settings.Settings}

Window{203614d u0 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}

Window{442b22 u0 com.android.systemui.wallpapers.ImageWallpaper}

相关推荐
程序员-珍2 小时前
Android studio 突然打不开
android·java
郑州光合科技余经理10 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
andyweike15 小时前
Android Automotive
android
又见情义16 小时前
记一次 RK3568 Android 13 蓝牙反复重启问题的排查与修复
android
hai_android16 小时前
Kotlin Flow combine 源码剖析:一个 Channel 如何优雅合并多条流
android·java·kotlin
敲代码的瓦龙17 小时前
Jetpack?ViewModel!!!
android·开发语言·kotlin·android-studio
Godikov18 小时前
Android 工控终端实战:从秒级卡顿到毫秒响应,SQLite/LitePal 性能优化全记录
android
星间都市山脉20 小时前
Android16 SystemService.onBootPhase 调用时机
android·java·linux·windows·ubuntu
swithun20 小时前
不用 WebView,我用 Kotlin + Compose Multiplatform 重写 Mermaid,并做了 2048 组对拍
android·开源·kotlin