美文网首页
Android12系统源码之NavigationBar和Stat

Android12系统源码之NavigationBar和Stat

作者: AFinalStone | 来源:发表于2022-11-25 16:09 被阅读0次

    前言

    NavigationBar 和 StatusBar 都属于 SystemBar,也叫做 decor,就是说给 App 装饰的意思。一般的 window 的布局是在 PhoneWindowManager 的 layoutWindowLw() 方法中,而 SystemBar 是在 beginLayoutLw() 方法中布局。

    当前最上层的 Activity 可以修改 SystemBar 的 visibility,可以调用 View#setSystemUiVisibility() 方法,系统也有一些针对 SystemBar visibility 的策略。最终的 visibility 保存在 PhoneWindowManager 中的 mLastSystemUiFlags 变量中。

    一、简单认识Android12中的DisplayFrames

    mDisplayId 是跟物理屏幕相关的,DEFAULT_DISPLAY 的值是 0,mDisplayWidth 是物理屏幕宽度,mDisplayHeight 是物理屏幕高度。

    public class DisplayFrames {
        public final int mDisplayId;
    
        public final InsetsState mInsetsState;
    
        /**
         * The current visible size of the screen; really; (ir)regardless of whether the status bar can
         * be hidden but not extending into the overscan area.
         */
        public final Rect mUnrestricted = new Rect();
    
        /**
         * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
         */
        public final Rect mDisplayCutoutSafe = new Rect();
    
        public int mDisplayWidth;
        public int mDisplayHeight;
    
        public int mRotation;
    
        public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
                WmDisplayCutout displayCutout, RoundedCorners roundedCorners,
                PrivacyIndicatorBounds indicatorBounds) {
            mDisplayId = displayId;
            mInsetsState = insetsState;
            onDisplayInfoUpdated(info, displayCutout, roundedCorners, indicatorBounds);
        }
    
        /**
         * Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
         *
         * @param info the updated {@link DisplayInfo}.
         * @param displayCutout the updated {@link DisplayCutout}.
         * @param roundedCorners the updated {@link RoundedCorners}.
         * @return {@code true} if the insets state has been changed; {@code false} otherwise.
         */
        public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout,
                @NonNull RoundedCorners roundedCorners,
                @NonNull PrivacyIndicatorBounds indicatorBounds) {
            mRotation = info.rotation;
    
            final InsetsState state = mInsetsState;
            final Rect safe = mDisplayCutoutSafe;
            final DisplayCutout cutout = displayCutout.getDisplayCutout();
            if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
                     && state.getDisplayCutout().equals(cutout)
                    && state.getRoundedCorners().equals(roundedCorners)
                    && state.getPrivacyIndicatorBounds().equals(indicatorBounds)) {
                return false;
            }
            mDisplayWidth = info.logicalWidth;
            mDisplayHeight = info.logicalHeight;
            final Rect unrestricted = mUnrestricted;
            unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
            safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
            state.setDisplayFrame(unrestricted);
            state.setDisplayCutout(cutout);
            state.setRoundedCorners(roundedCorners);
            state.setPrivacyIndicatorBounds(indicatorBounds);
            if (!cutout.isEmpty()) {
                if (cutout.getSafeInsetLeft() > 0) {
                    safe.left = unrestricted.left + cutout.getSafeInsetLeft();
                }
                if (cutout.getSafeInsetTop() > 0) {
                    safe.top = unrestricted.top + cutout.getSafeInsetTop();
                }
                if (cutout.getSafeInsetRight() > 0) {
                    safe.right = unrestricted.right - cutout.getSafeInsetRight();
                }
                if (cutout.getSafeInsetBottom() > 0) {
                    safe.bottom = unrestricted.bottom - cutout.getSafeInsetBottom();
                }
                state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
                        unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
                state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
                        unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
                state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
                        safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
                state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
                        unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
            } else {
                state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
                state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
                state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
                state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
            }
            return true;
        }
    
        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
            final long token = proto.start(fieldId);
            proto.end(token);
        }
    
        public void dump(String prefix, PrintWriter pw) {
            pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
                    + " r=" + mRotation);
        }
    }
    

    二、NavigationBar 的布局

    1、Android12系统主要是在 beginLayoutLw() 方法中对系统的SystemBar进行布局的。

     public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
            if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
                mNavigationBarPosition = layoutNavigationBar(displayFrames,
                        mBarContentFrames.get(TYPE_NAVIGATION_BAR));
                return;
            }
            if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
                layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
                return;
            }
            if (win.mActivityRecord != null && win.mActivityRecord.mWaitForEnteringPinnedMode) {
                // Skip layout of the window when in transition to pip mode.
                return;
            }
            final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
    
            final int type = attrs.type;
            final int fl = attrs.flags;
            final int pfl = attrs.privateFlags;
            final int sim = attrs.softInputMode;
    
            displayFrames = win.getDisplayFrames(displayFrames);
            final WindowFrames windowFrames = win.getLayoutingWindowFrames();
    
            sTmpLastParentFrame.set(windowFrames.mParentFrame);
            final Rect pf = windowFrames.mParentFrame;
            final Rect df = windowFrames.mDisplayFrame;
            windowFrames.setParentFrameWasClippedByDisplayCutout(false);
    
            final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
            final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
    
            final InsetsState state = win.getInsetsState();
            if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) {
                // Override the bounds in window token has many side effects. Directly use the display
                // frame set for the simulated layout for this case.
                computeWindowBounds(attrs, state, df, df);
            } else {
                computeWindowBounds(attrs, state, win.getBounds(), df);
            }
            if (attached == null) {
                pf.set(df);
                if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
                    final InsetsSource source = state.peekSource(ITYPE_IME);
                    if (source != null) {
                        pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
                    }
                }
            } else {
                pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
            }
    
            final int cutoutMode = attrs.layoutInDisplayCutoutMode;
            // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
            // the cutout safe zone.
            if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
                final boolean attachedInParent = attached != null && !layoutInScreen;
                final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
                final boolean requestedHideNavigation =
                        !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
    
                // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
                // get cropped / shifted to the displayFrame in WindowState.
                final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
                        && type != TYPE_BASE_APPLICATION;
                final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
                displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
                if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
                    if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
                        displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
                        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
                    } else {
                        displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
                        displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
                    }
                }
    
                if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
                        && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                        || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                    // At the top we have the status bar, so apps that are
                    // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
                    // already expect that there's an inset there and we don't need to exclude
                    // the window from that area.
                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
                }
                if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
                        && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                        || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                    // Same for the navigation bar.
                    switch (mNavigationBarPosition) {
                        case NAV_BAR_BOTTOM:
                            displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
                            break;
                        case NAV_BAR_RIGHT:
                            displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
                            break;
                        case NAV_BAR_LEFT:
                            displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
                            break;
                    }
                }
                if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
                    // The IME can always extend under the bottom cutout if the navbar is there.
                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
                }
                // Windows that are attached to a parent and laid out in said parent already avoid
                // the cutout according to that parent and don't need to be further constrained.
                // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
                // They will later be cropped or shifted using the displayFrame in WindowState,
                // which prevents overlap with the DisplayCutout.
                if (!attachedInParent && !floatingInScreenWindow) {
                    sTmpRect.set(pf);
                    pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
                    windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
                }
                // Make sure that NO_LIMITS windows clipped to the display don't extend under the
                // cutout.
                df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
            }
    
            // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
            // Also, we don't allow windows in multi-window mode to extend out of the screen.
            if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
                    && !win.inMultiWindowMode()) {
                df.left = df.top = -10000;
                df.right = df.bottom = 10000;
            }
    
            if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
                    + ": sim=#" + Integer.toHexString(sim)
                    + " attach=" + attached + " type=" + type
                    + String.format(" flags=0x%08x", fl)
                    + " pf=" + pf.toShortString() + " df=" + df.toShortString());
    
            if (!sTmpLastParentFrame.equals(pf)) {
                windowFrames.setContentChanged(true);
            }
    
            win.computeFrameAndUpdateSourceFrame(displayFrames);
            if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) {
                if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
                    // Make sure that the zone we're avoiding for the cutout is at least as tall as the
                    // status bar; otherwise fullscreen apps will end up cutting halfway into the status
                    // bar.
                    displayFrames.mDisplayCutoutSafe.top = Math.max(
                            displayFrames.mDisplayCutoutSafe.top,
                            windowFrames.mFrame.bottom);
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:Android12系统源码之NavigationBar和Stat

          本文链接:https://www.haomeiwen.com/subject/wtjwxdtx.html