android13-release
设置->系统->手势->系统导航->“三按钮”导航
- 设置中:
“三按钮”导航
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
packages/apps/Settings/res/values-zh-rCN/strings.xml
"“三按钮”导航"
- 默认导航栏模式:
config_navBarInteractionMode
frameworks/base/core/res/res/values/config.xml<integer name="config_navBarInteractionMode">0integer> <bool name="config_showNavigationBar">falsebool>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- Settings数据库中:
adb shell settings get Secure navigation_mode
frameworks/base/core/java/android/provider/Settings.java/** * Navigation bar mode. * 0 = 3 button * 1 = 2 button * 2 = fully gestural * @hide */ @Readable public static final String NAVIGATION_MODE = "navigation_mode";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- prop属性
"qemu.hw.mainkeys"
:允许系统属性覆盖此设置。由仿真器使用。使用方法hasNavigationBar()
- 导航栏高度:
navigation_bar_height
frameworks/base/core/res/res/values/dimens.xml<dimen name="navigation_bar_height">48dpdimen> <dimen name="navigation_bar_height_landscape">48dpdimen>
- 1
- 2
- 3
- 4
设置中显示判断:
String NAV_BAR_MODE_3BUTTON_OVERLAY = "com.android.internal.systemui.navbar.threebutton";
:
/product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apkString NAV_BAR_MODE_2BUTTON_OVERLAY = "com.android.internal.systemui.navbar.twobutton";
:
/product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apkString NAV_BAR_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.navbar.gestural";
:
/product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
static boolean isOverlayPackageAvailable(Context context, String overlayPackage) {
try {
return context.getPackageManager().getPackageInfo(overlayPackage, 0) != null;
} catch (PackageManager.NameNotFoundException e) {
// Not found, just return unavailable
return false;
}
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
private int getCurrentInteractionMode(Context context) {
int mode = context.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
if (DEBUG) {
Log.d(TAG, "getCurrentInteractionMode: mode=" + mode
+ " contextUser=" + context.getUserId());
}
return mode;
}
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
if (DEBUG) {
Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
dumpAssetPaths(mCurrentUserContext);
}
if (notify) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNavigationModeChanged(mode);
}
}
}
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
if (mDisplayContent.isDefaultDisplay) {
mHasStatusBar = true;
mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
mHasNavigationBar = true;
}
} else {
mHasStatusBar = false;
mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
}
OverlayManagerService 运行时资源叠加层 (RRO)
点击设置后,导航栏模式通过OverlayManagerService
服务对config_navBarInteractionMode
资源进行叠加,而settings的Secure表中navigation_mode
属性只是记录模式。
frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java
资源叠加主要文件:config.xml
frameworks/base/core/res/res/values/config.xml
/product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apk
/product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apk
/product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
updateActivityManager(affectedPackages, userId)
:发送受覆盖状态更改影响的所有目标包的配置更改事件。broadcastActionOverlayChanged(targets, userId)
:发送覆盖包已更改广播ACTION_OVERLAY_CHANGED。
private void updateTargetPackagesLocked(@Nullable Set<PackageAndUser> updatedTargets) {
if (CollectionUtils.isEmpty(updatedTargets)) {
return;
}
persistSettingsLocked();
final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
for (int i = 0, n = userTargets.size(); i < n; i++) {
final ArraySet<String> targets = userTargets.valueAt(i);
final int userId = userTargets.keyAt(i);
final List<String> affectedPackages = updatePackageManagerLocked(targets, userId);
if (affectedPackages.isEmpty()) {
// The package manager paths are already up-to-date.
continue;
}
FgThread.getHandler().post(() -> {
// Send configuration changed events for all target packages that have been affected
// by overlay state changes.
updateActivityManager(affectedPackages, userId);
// Do not send broadcasts for all affected targets. Overlays targeting the framework
// or shared libraries may cause too many broadcasts to be sent at once.
broadcastActionOverlayChanged(targets, userId);
});
}
}
mReceiver :监听ACTION_OVERLAY_CHANGED广播
Secure.NAVIGATION_MODE:记录导航栏模式改变值
mListeners.get(i).onNavigationModeChanged(mode):通知导航栏模式改变的ModeChangedListener
监听
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.d(TAG, "ACTION_OVERLAY_CHANGED");
}
updateCurrentInteractionMode(true /* notify */);
}
};
public NavigationModeController(Context context,
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
@UiBackground Executor uiBgExecutor,
DumpManager dumpManager) {
//... ...
IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
//... ...
}
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
if (DEBUG) {
Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
dumpAssetPaths(mCurrentUserContext);
}
if (notify) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNavigationModeChanged(mode);
}
}
}
NavigationModeController
通知监听执行onNavigationModeChanged
方法更新,最后navBar.getView().updateStates()
执行更新界面NavigationBarView
:
updateSlippery()
:更新WindowManager.LayoutParams.FLAG_SLIPERY
状态,具体取决于是否启用了向上滑动,或者通知是否在未处于动画状态的情况下完全打开。如果启用了slide,触摸事件将离开导航栏窗口并进入全屏应用程序/主页窗口,如果没有,则手势离开导航栏后,导航栏将收到取消的触摸事件。reloadNavIcons()
:重新导入导航栏相关图片资源updateNavButtonIcons
:更新界面导航栏图标、显示状态,及活动触摸区域
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@Override
public void onNavigationModeChanged(int mode) {
if (mNavMode == mode) {
return;
}
final int oldMode = mNavMode;
mNavMode = mode;
updateAccessibilityButtonModeIfNeeded();
mHandler.post(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
updateNavbarForTaskbar();
}
for (int i = 0; i < mNavigationBars.size(); i++) {
NavigationBar navBar = mNavigationBars.valueAt(i);
if (navBar == null) {
continue;
}
navBar.getView().updateStates();
}
});
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
public void updateStates() {
if (mNavigationInflaterView != null) {
// Reinflate the navbar if needed, no-op unless the swipe up state changes
mNavigationInflaterView.onLikelyDefaultLayoutChange();
}
updateSlippery();
reloadNavIcons();
updateNavButtonIcons();
WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(
!mShowSwipeUpUi);
getHomeButton().setAccessibilityDelegate(
mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
}
frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml
<com.android.systemui.navigationbar.NavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_bar_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:background="@drawable/system_bar_background">
<com.android.systemui.navigationbar.NavigationBarInflaterView
android:id="@+id/navigation_inflater"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false" />
com.android.systemui.navigationbar.NavigationBarView>
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
View v = null;
String button = extractButton(buttonSpec);
if (LEFT.equals(button)) {
button = extractButton(NAVSPACE);
} else if (RIGHT.equals(button)) {
button = extractButton(MENU_IME_ROTATE);
}
if (HOME.equals(button)) {
v = inflater.inflate(R.layout.home, parent, false);
} else if (BACK.equals(button)) {
v = inflater.inflate(R.layout.back, parent, false);
} else if (RECENT.equals(button)) {
v = inflater.inflate(R.layout.recent_apps, parent, false);
} else if (MENU_IME_ROTATE.equals(button)) {
v = inflater.inflate(R.layout.menu_ime, parent, false);
} else if (NAVSPACE.equals(button)) {
v = inflater.inflate(R.layout.nav_key_space, parent, false);
} else if (CLIPBOARD.equals(button)) {
v = inflater.inflate(R.layout.clipboard, parent, false);
} else if (CONTEXTUAL.equals(button)) {
v = inflater.inflate(R.layout.contextual, parent, false);
} else if (HOME_HANDLE.equals(button)) {
v = inflater.inflate(R.layout.home_handle, parent, false);
} else if (IME_SWITCHER.equals(button)) {
v = inflater.inflate(R.layout.ime_switcher, parent, false);
} else if (button.startsWith(KEY)) {
String uri = extractImage(button);
int code = extractKeycode(button);
v = inflater.inflate(R.layout.custom_key, parent, false);
((KeyButtonView) v).setCode(code);
if (uri != null) {
if (uri.contains(":")) {
((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
} else if (uri.contains("/")) {
int index = uri.indexOf('/');
String pkg = uri.substring(0, index);
int id = Integer.parseInt(uri.substring(index + 1));
((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
}
}
}
return v;
}
配置文件:frameworks/base/packages/SystemUI/res/values/config.xml
<string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]string> <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]string> <string name="config_navBarLayoutHandle" translatable="false">back[70AC];home_handle;ime_switcher[70AC]string>
- 1
- 2
- 3
- 4
frameworks/base/packages/SystemUI/res/layout/home.xml
frameworks/base/packages/SystemUI/res/layout/back.xml
frameworks/base/packages/SystemUI/res/layout/recent_apps.xml
frameworks/base/packages/SystemUI/res/layout/menu_ime.xml
frameworks/base/packages/SystemUI/res/layout/nav_key_space.xml
frameworks/base/packages/SystemUI/res/layout/contextual.xml
frameworks/base/packages/SystemUI/res/layout/home_handle.xml
frameworks/base/packages/SystemUI/res/layout/ime_switcher.xml
frameworks/base/packages/SystemUI/res/layout/custom_key.xml
通过对应
xml
布局id
,将布局给到对应id
的ButtonDispatcher
,xml中布局自定对象frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
@Override
public void onFinishInflate() {
super.onFinishInflate();
mNavigationInflaterView = findViewById(R.id.navigation_inflater);
mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
updateOrientationViews();
reloadNavIcons();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
public void setButtonDispatchers(SparseArray<ButtonDispatcher> buttonDispatchers) {
mButtonDispatchers = buttonDispatchers;
clearDispatcherViews();
for (int i = 0; i < buttonDispatchers.size(); i++) {
initiallyFill(buttonDispatchers.valueAt(i));
}
}
private void initiallyFill(ButtonDispatcher buttonDispatcher) {
addAll(buttonDispatcher, mHorizontal.findViewById(R.id.ends_group));
addAll(buttonDispatcher, mHorizontal.findViewById(R.id.center_group));
addAll(buttonDispatcher, mVertical.findViewById(R.id.ends_group));
addAll(buttonDispatcher, mVertical.findViewById(R.id.center_group));
}
private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) {
for (int i = 0; i < parent.getChildCount(); i++) {
// Need to manually search for each id, just in case each group has more than one
// of a single id. It probably mostly a waste of time, but shouldn't take long
// and will only happen once.
if (parent.getChildAt(i).getId() == buttonDispatcher.getId()) {
buttonDispatcher.addView(parent.getChildAt(i));
}
if (parent.getChildAt(i) instanceof ViewGroup) {
addAll(buttonDispatcher, (ViewGroup) parent.getChildAt(i));
}
}
}
back、home在布局中配置
keyCode
,通过onTouchEvent
触摸判断,sendEvent
发送注入事件mInputManager.injectInputEvent()
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@Override
public void setOnClickListener(OnClickListener onClickListener) {
super.setOnClickListener(onClickListener);
mOnClickListener = onClickListener;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
final int action = ev.getAction();
int x, y;
if (action == MotionEvent.ACTION_DOWN) {
mGestureAborted = false;
}
if (mGestureAborted) {
setPressed(false);
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
setPressed(true);
mTouchDownX = (int) ev.getX();
mTouchDownY = (int) ev.getY();
if (mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
if (!showSwipeUI) {
playSoundEffect(SoundEffectConstants.CLICK);
}
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
case MotionEvent.ACTION_MOVE:
x = (int) ev.getX();
y = (int) ev.getY();
float slop = QuickStepContract.getQuickStepTouchSlopPx(getContext());
if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) {
// When quick step is enabled, prevent animating the ripple triggered by
// setPressed and decide to run it on touch up
setPressed(false);
removeCallbacks(mCheckLongPress);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
if (mCode != KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
removeCallbacks(mCheckLongPress);
break;
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
if (showSwipeUI) {
if (doIt) {
// Apply haptic feedback on touch up since there is none on touch down
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
playSoundEffect(SoundEffectConstants.CLICK);
}
} else if (doHapticFeedback && !mLongClicked) {
// Always send a release ourselves because it doesn't seem to be sent elsewhere
// and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if (mCode != KEYCODE_UNKNOWN) {
if (doIt) {
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
} else {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
} else {
// no key code, just a regular ImageView
if (doIt && mOnClickListener != null) {
mOnClickListener.onClick(this);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
}
removeCallbacks(mCheckLongPress);
break;
}
return true;
}
private void sendEvent(int action, int flags, long when) {
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(mCode)
.addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action)
.addTaggedData(MetricsEvent.FIELD_FLAGS, flags));
logSomePresses(action, flags);
if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action));
if (action == MotionEvent.ACTION_UP) {
mOverviewProxyService.notifyBackAction((flags & KeyEvent.FLAG_CANCELED) == 0,
-1, -1, true /* isButton */, false /* gestureSwipeLeft */);
}
}
final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
int displayId = INVALID_DISPLAY;
// Make KeyEvent work on multi-display environment
if (getDisplay() != null) {
displayId = getDisplay().getDisplayId();
}
if (displayId != INVALID_DISPLAY) {
ev.setDisplayId(displayId);
}
mInputManager.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
private void prepareNavigationBarView() {
mView.reorient();
ButtonDispatcher recentsButton = mView.getRecentsButton();
recentsButton.setOnClickListener(this::onRecentsClick);
recentsButton.setOnTouchListener(this::onRecentsTouch);
ButtonDispatcher homeButton = mView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
reconfigureHomeLongClick();
ButtonDispatcher accessibilityButton = mView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
updateAccessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
updateScreenPinningGestures();
}
private void updateScreenPinningGestures() {
// Change the cancel pin gesture to home and back if recents button is invisible
boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
ButtonDispatcher backButton = mView.getBackButton();
ButtonDispatcher recentsButton = mView.getRecentsButton();
if (pinningActive) {
boolean recentsVisible = mView.isRecentsButtonVisible();
backButton.setOnLongClickListener(recentsVisible
? this::onLongPressBackRecents
: this::onLongPressBackHome);
recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
} else {
backButton.setOnLongClickListener(null);
recentsButton.setOnLongClickListener(null);
}
// Note, this needs to be set after even if we're setting the listener to null
backButton.setLongClickable(pinningActive);
recentsButton.setLongClickable(pinningActive);
}
2023-10-22 01:11:08.481 791-791 KeyButtonView com.android.systemui I Back button event: ACTION_DOWN
2023-10-22 01:11:08.576 791-791 KeyButtonView com.android.systemui I Back button event: ACTION_UP
2023-10-22 01:11:12.156 791-791 ShadeControllerImpl com.android.systemui V NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
2023-10-22 01:11:12.196 791-791 ShadeControllerImpl com.android.systemui V NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
2023-10-22 01:11:19.590 791-791 ShadeControllerImpl com.android.systemui V NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
2023-10-22 01:11:19.642 791-791 ShadeControllerImpl com.android.systemui V NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false