一起看看StatusBarManagerService(二)

写在前面

StatusBarManagerService中API涉及systemui的多个模块;本篇主要介绍StatusBarManagerService中与状态栏相关的API。


状态栏相关函数解析

1)以Android U为例进行代码分析
2)displayId标识的是当前回调的屏幕是哪一个;手机、pad、折叠屏,返回的displayId均为0,即当前正在展示的主屏幕。

1.disable

void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,boolean animate)

该方法用于标识当前状态下systemui中各个控件、功能的禁用状态,当某个状态改变时,回调该方法;不同的状态在state中对应不同的二进制位。
不同的位在View中和StatusBarManager中可以查找到;state1中的mask在View中,新增state2后,在StatusBarManager声明了与View中对应的mask,同时新增了state2的mask。
StatusBarManager标识位

状态栏主要是负责显示系统的一些状态、触发下拉(状态栏和通知栏在同一个面板上),这里主要有两个实现了回调的地方:
1)CollapsedStatusBarFragment

@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
    if (displayId != getContext().getDisplayId()) {
        return;
    }
    // log
    mCollapsedStatusBarFragmentLogger
            .logDisableFlagChange(new DisableState(state1, state2));
    // 前文说过flag通过转成int的二进制位来标记;这里将对应二进制位又转成了boolean值
    mLastSystemVisibility =
            StatusBarVisibilityModel.createModelFromFlags(state1, state2);
    // 更新显示
    updateStatusBarVisibilities(animate);
}

// 状态栏对应的显示:时钟、通知图标、系统信息(电量、信号等)
@JvmStatic
fun createModelFromFlags(disabled1: Int, disabled2: Int): StatusBarVisibilityModel {
    return StatusBarVisibilityModel(
        showClock = (disabled1 and DISABLE_CLOCK) == 0,
        showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0,
        // TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the
        //  value of [OngoingCallController]. Do we need to process the flag here?
        showOngoingCallChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
        showSystemInfo =
            (disabled1 and DISABLE_SYSTEM_INFO) == 0 &&
                (disabled2 and DISABLE2_SYSTEM_ICONS) == 0,
    )
}

// 更新显示
private void updateStatusBarVisibilities(boolean animate) {
    StatusBarVisibilityModel previousModel = mLastModifiedVisibility;
    // mLastSystemVisibility为上面通过disable flag转成的boolean值;
    // 最终的显示与否,还需要加上一些其他变量计算,结果通过该model保存
    StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility);
    mCollapsedStatusBarFragmentLogger.logVisibilityModel(newModel);
    mLastModifiedVisibility = newModel;

    // 根据计算出的显示与否,和当前的显示状态,判断是否需要重新设置visibility,以及动画等

    if (newModel.getShowSystemInfo() != previousModel.getShowSystemInfo()) {
        if (newModel.getShowSystemInfo()) {
            showEndSideContent(animate);
            showOperatorName(animate);
        } else {
            hideEndSideContent(animate);
            hideOperatorName(animate);
        }
    }

    // The ongoing call chip and notification icon visibilities are intertwined, so update both
    // if either change.
    if (newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons()
            || newModel.getShowOngoingCallChip() != previousModel.getShowOngoingCallChip()) {
        updateNotificationIconAreaAndCallChip(animate);
    }

    // The clock may have already been hidden, but we might want to shift its
    // visibility to GONE from INVISIBLE or vice versa
    if (newModel.getShowClock() != previousModel.getShowClock()
            || mClockView.getVisibility() != clockHiddenMode()) {
        if (newModel.getShowClock()) {
            showClock(animate);
        } else {
            hideClock(animate);
        }
    }
}

2)CentralSurfacesCommandQueueCallbacks

@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
    if (displayId != mDisplayId) {
        return;
    }

    int state2BeforeAdjustment = state2;
    state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
    // log
    ...

    // 这里与上面的方法不同,没使用model保存,直接通过flag和mask进行计算;
    // U之前的版本也是采用该方式
    // 判断的方法为:收起通知栏面板、退出悬浮通知、更新quick setting面板是否能够展开
    
    final int old1 = mCentralSurfaces.getDisabled1();
    final int diff1 = state1 ^ old1;
    mCentralSurfaces.setDisabled1(state1);

    final int old2 = mCentralSurfaces.getDisabled2();
    final int diff2 = state2 ^ old2;
    mCentralSurfaces.setDisabled2(state2);

    if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
        if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
            mShadeController.animateCollapseShade();
        }
    }

    if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
        if (mCentralSurfaces.areNotificationAlertsDisabled()) {
            mHeadsUpManager.releaseAllImmediately();
        }
    }

    if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
        mCentralSurfaces.updateQsExpansionEnabled();
    }

    if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
        mCentralSurfaces.updateQsExpansionEnabled();
        if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
            mShadeController.animateCollapseShade();
        }
    }

    mShadeViewController.disableHeader(state1, state2, animate);
}
2.setIcon/removeIcon

状态栏可以显示系统的图标;系统提供了三方可以显示的api,需要将图标加入到状态栏,再控制visibility;同时系统也会对icon进行一些显示策略的定制;设置的图标会通过slot name进行区分。

1)StatusBarIconControllerImpl中mCommandQueueCallbacks,通过调用CommandQueue.addCallback注册监听:

private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
    @Override
    public void setIcon(String slot, StatusBarIcon icon) {
        // Icons that come from CommandQueue are from external services.
        setExternalIcon(slot, icon);
    }

    @Override
    public void removeIcon(String slot) {
        removeAllIconsForExternalSlot(slot);
    }
};

2)set和remove的调用路径类似,这里以set为例进行追踪:

private void setExternalIcon(String slot, StatusBarIcon icon) {
    if (icon == null) {
        removeAllIconsForExternalSlot(slot);
        return;
    }
    // 状态栏的图标根据slotName进行区分
    String slotName = createExternalSlotName(slot);
    StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
    setIcon(slotName, holder);
}

private void setIcon(String slot, @NonNull StatusBarIconHolder holder) {
    boolean isNew = mStatusBarIconList.getIconHolder(slot, holder.getTag()) == null;
    mStatusBarIconList.setIcon(slot, holder);

    // isNew标记是否当前图标已经set进去了,填加和更新两种不同的操作
    // 以填加为例追踪
    if (isNew) {
        addSystemIcon(slot, holder);
    } else {
        handleSet(slot, holder);
    }
}

private void addSystemIcon(String slot, StatusBarIconHolder holder) {
    int viewIndex = mStatusBarIconList.getViewIndex(slot, holder.getTag());
    boolean hidden = mIconHideList.contains(slot);

    // mIconGroups为IconManager的集合,用于管理icon
    mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}

// StatusBarIconController.java

protected void onIconAdded(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
    addHolder(index, slot, blocked, holder);
}

// 根据填加的icon不同调用方法不同;
// systemui中常驻的icon中,信号、wifi有特殊的操作,显示规则更复杂,有单独的方法;
// 可以通过三方填加的icon,会嗲用addIcon函数
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
        StatusBarIconHolder holder) {
    // This is a little hacky, and probably regrettable, but just set `blocked` on any icon
    // that is in our blocked list, then we'll never see it
    if (mBlockList.contains(slot)) {
        blocked = true;
    }
    switch (holder.getType()) {
        case TYPE_ICON:
            return addIcon(index, slot, blocked, holder.getIcon());

        case TYPE_WIFI:
            return addWifiIcon(index, slot, holder.getWifiState());

        case TYPE_WIFI_NEW: // todo dzm-u
            return addNewWifiIcon(index, slot);

        case TYPE_MOBILE:
            return addMobileIcon(index, slot, holder.getMobileState());

        case TYPE_MOBILE_NEW: // todo dzm-u
            return addNewMobileIcon(index, slot, holder.getTag());
    }

    return null;
}

// mGroup为IconManager初始化传进来的参数,为当前icon会展示的区域;
// 此处对应R.id.statusIcons,system_icons内
@VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
        StatusBarIcon icon) {
    StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
    view.set(icon);
    mGroup.addView(view, index, onCreateLayoutParams());
    return view;
}
3.setWindowState

void setWindowState(int displayId, @WindowType int window,@WindowVisibleState int state)

用于回调statusBar和navigationBar的显示状态改变;
WindowType对应StatusBarManager#WINDOW_STATUS_BAR和StatusBarManager#WINDOW_NAVIGATION_BAR(0和1),用于区分状态栏和导航栏的回调;
WindowVisibleState对应
public static final int WINDOW_STATE_SHOWING = 0;
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;

// StatusBarWindowStateController.java

private fun setWindowState(
    displayId: Int,
    @StatusBarManager.WindowType window: Int,
    @WindowVisibleState state: Int
) {
    if (displayId != thisDisplayId) {
        return
    }
    if (window != WINDOW_STATUS_BAR) {
        return
    }
    if (windowState == state) {
        return
    }

    windowState = state
    if (CentralSurfaces.DEBUG_WINDOW_STATE) {
        Log.d(CentralSurfaces.TAG, "Status bar " + windowStateToString(state))
    }
    listeners.forEach { it.onStatusBarWindowStateChanged(state) }
}

// CentralSurfacesImpl.java

void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
    // 更新bubble的显示
    updateBubblesVisibility();
    mStatusBarWindowState = state;
}

// PhoneStatusBarViewController.kt

fun onTouch(event: MotionEvent) {
    // 当前状态栏的window state会影响下拉
    if (centralSurfaces.statusBarWindowState == WINDOW_STATE_SHOWING) {
        val upOrCancel =
                event.action == MotionEvent.ACTION_UP ||
                event.action == MotionEvent.ACTION_CANCEL
        centralSurfaces.setInteracting(WINDOW_STATUS_BAR,
                !upOrCancel || shadeController.isExpandedVisible)
    }
}
4. setTopAppHidesStatusBar

void setTopAppHidesStatusBar(boolean topAppHidesStatusBar)
fw通过window的flag去判断是否显示statusbar,当top activity改变,且对应的flag变时,会回调;
此处有一个情况,dialog在activity中启动,读取到的是activity的,dialog的window的设置不会被应用;
三方应用有此问题,需要三方去适配,原生设计如此。

// CentralSurfacesCommandQueueCallbacks

@Override
public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
    mStatusBarHideIconsForBouncerManager
            .setTopAppHidesStatusBarAndTriggerUpdate(topAppHidesStatusBar);
}

// StatusBarHideIconsForBouncerManager.java

fun setTopAppHidesStatusBarAndTriggerUpdate(topAppHidesStatusBar: Boolean) {
    this.topAppHidesStatusBar = topAppHidesStatusBar
    if (!topAppHidesStatusBar && wereIconsJustHidden) {
        // Immediately update the icon hidden state, since that should only apply if we're
        // staying fullscreen.
        wereIconsJustHidden = false
        // 触发重新计算disable flag
        commandQueue.recomputeDisableFlags(displayId, /* animate= */ true)
    }
    updateHideIconsForBouncer(animate = true)
}

/**
 * Updates whether the status bar icons should be hidden in the bouncer. May trigger
 * [commandQueue.recomputeDisableFlags] if the icon visibility status changes.
 */
private fun updateHideIconsForBouncer(animate: Boolean) {
    val hideBecauseApp =
        // here!!! 
        topAppHidesStatusBar &&
                isOccluded &&
                (statusBarWindowHidden || bouncerShowing)
    val hideBecauseKeyguard = !panelExpanded && !isOccluded && bouncerShowing
    val shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard
    if (hideIconsForBouncer != shouldHideIconsForBouncer) {
        hideIconsForBouncer = shouldHideIconsForBouncer
        if (!shouldHideIconsForBouncer && bouncerWasShowingWhenHidden) {
            // We're delaying the showing, since most of the time the fullscreen app will
            // hide the icons again and we don't want them to fade in and out immediately again.
            // topAppHidesStatusBar最终影响状态栏的显示
            wereIconsJustHidden = true
            mainExecutor.executeDelayed(
                {
                    wereIconsJustHidden = false
                    commandQueue.recomputeDisableFlags(displayId, true)
                },
                500
            )
        } else {
            commandQueue.recomputeDisableFlags(displayId, animate)
        }
    }
    if (shouldHideIconsForBouncer) {
        bouncerWasShowingWhenHidden = bouncerShowing
    }
}

// CollapsedStatusBarFragment

private boolean shouldHideStatusBar() {
    ...
    // 根据wereIconsJustHidden判断
    return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
}

protected StatusBarVisibilityModel calculateInternalModel(
        StatusBarVisibilityModel externalModel) {
    ...

    if (...
            && shouldHideStatusBar()
            ...) {
        // Hide everything
        return new StatusBarVisibilityModel(
                /* showClock= */ false,
                /* showNotificationIcons= */ false,
                /* showOngoingCallChip= */ false,
                /* showSystemInfo= */ false);
    }
    ...
}
5.onSystemBarAttributesChanged

方法实现在StatusBar.java中,当状态栏的背景颜色/图标颜色对应的设置/影响区域改变时回调
appearence和behavior注解如下:@Appearence
WindowInsetsController.java
int APPEARANCE_OPAQUE_STATUS_BARS = 1;
int APPEARANCE_OPAQUE_NAVIGATION_BARS = 1 << 1;
int APPEARANCE_LOW_PROFILE_BARS = 1 << 2;
int APPEARANCE_LIGHT_STATUS_BARS = 1 << 3;
int APPEARANCE_LIGHT_NAVIGATION_BARS = 1 << 4;
int APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS = 1 << 5;
int APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS = 1 << 6;
StatusBar只对应两种颜色,透明和半透明

@Behavior
@Deprecated
int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;
int BEHAVIOR_DEFAULT = 1;
@Deprecated
int BEHAVIOR_SHOW_BARS_BY_SWIPE = BEHAVIOR_DEFAULT;
int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;
方法中未使用到该变量

appearanceRegion中保存着区域Rect和appearence,在横屏分屏情况下,左右分屏对状态栏的设置不一样时,状态栏图标的颜色需要根据所在区域的设置,来决定显示的颜色
参数中的appearence标识状态栏的背景颜色应该如何显示;appearanceRegions数组用来标识状态栏的图标颜色应该如何显示

// CentralSurfacesCommandQueueCallbacks

@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
        AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
        @Behavior int behavior, @InsetsType int requestedVisibleTypes, String packageName,
        LetterboxDetails[] letterboxDetails) {
    if (displayId != mDisplayId) {
        return;
    }
    // SystemBarAttributesListener should __always__ be the top-level listener for system bar
    // attributes changed.
    mSystemBarAttributesListener.onSystemBarAttributesChanged(
            displayId,
            appearance,
            appearanceRegions,
            navbarColorManagedByIme,
            behavior,
            requestedVisibleTypes,
            packageName,
            letterboxDetails
    );
}

// SystemBarAttributesListener.kt

fun onSystemBarAttributesChanged(
        displayId: Int,
        @Appearance originalAppearance: Int,
        originalAppearanceRegions: Array<AppearanceRegion>,
        navbarColorManagedByIme: Boolean,
        @Behavior behavior: Int,
        @InsetsType requestedVisibleTypes: Int,
        packageName: String,
        letterboxDetails: Array<LetterboxDetails>
) {
    lastSystemBarAttributesParams =
        SystemBarAttributesParams(
            displayId,
            originalAppearance,
            originalAppearanceRegions.toList(),
            navbarColorManagedByIme,
            behavior,
            requestedVisibleTypes,
            packageName,
            letterboxDetails.toList())

    val (appearance, appearanceRegions) =
        modifyAppearanceIfNeeded(
            originalAppearance, originalAppearanceRegions, letterboxDetails)

    // 更新barMode,@TransitionMode
    val barModeChanged = centralSurfaces.setAppearance(appearance)

    // 更新状态栏颜色
    lightBarController.onStatusBarAppearanceChanged(
        appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)

    centralSurfaces.updateBubblesVisibility()
    statusBarStateController.setSystemBarAttributes(
        appearance, behavior, requestedVisibleTypes, packageName)
}

// LightBarController

void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged,
        int statusBarMode, boolean navbarColorManagedByIme) {
    final int numStacks = appearanceRegions.length;
    // 判断当前的展示区域与之前保存的展示区域是否一样mAppearanceRegions
    boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
    for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
        stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
    }
    // 当状态栏的背景色改变,或影响区域改变,图标颜色都需要重新计算
    if (stackAppearancesChanged || sbModeChanged || mIsCustomizingForBackNav) {
        mAppearanceRegions = appearanceRegions;
        onStatusBarModeChanged(statusBarMode);
        mIsCustomizingForBackNav = false;
    }
    mNavbarColorManagedByIme = navbarColorManagedByIme;
}

void onStatusBarModeChanged(int newBarMode) {
    mStatusBarMode = newBarMode;
    updateStatus(mAppearanceRegions);
}

private void updateStatus() {
    final int numStacks = mAppearanceRegions.length;
    int numLightStacks = 0;
 
    // We can only have maximum one light stack.
    int indexLightStack = -1;
 
    for (int i = 0; i < numStacks; i++) {
        // isLight判断状态栏的背景是否属于light
        // 计算light的区域数量
        if (isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
                APPEARANCE_LIGHT_STATUS_BARS)) {
            numLightStacks++;
            indexLightStack = i;
        }
    }
 
    // 所有区域都是light的,图标都为dark
    // setIconsDarkArea为null,图标设置颜色时不会去判断是否在某个区域
    // If all stacks are light, all icons get dark.
    if (numLightStacks == numStacks) {
        mStatusBarIconController.setIconsDarkArea(null);
        mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
 
    }
 
    // 所有区域都是dark的,图标都为light
    // If no one is light, all icons become white.
    else if (numLightStacks == 0) {
        mStatusBarIconController.getTransitionsController().setIconsDark(
                false, animateChange());
    }
 
    // 区域展示不同,设置light的区域
    // Not the same for every stack, magic!
    else {
        mStatusBarIconController.setIconsDarkArea(
                mAppearanceRegions[indexLightStack].getBounds());
        mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
    }
}
6.showTransient/abortTransient

void abortTransient(int displayId, @InternalInsetsType int[] types)

当应用全屏时,隐藏了状态栏;在手机上方下拉,状态栏会短暂的出现再收回,此时回调的就是这两个方法。
状态栏的出现/消失做的动画,由fw控制;回调是改变状态栏的背景颜色和图标颜色

@Override
public void showTransient(int displayId, @InternalInsetsType int[] types) {
    if (displayId != mDisplayId) {
        return;
    }
    if (!containsType(types, ITYPE_STATUS_BAR)) {
        return;
    }
    showTransientUnchecked();
}
 
private void showTransientUnchecked() {
    if (!mTransientShown) {
        // 标记为true
        mTransientShown = true;
        mNoAnimationOnNextBarModeChange = true;
        maybeUpdateBarMode();
    }
}
 
private void maybeUpdateBarMode() {
    final int barMode = barMode(mTransientShown, mAppearance);
    // 重新计算状态栏的背景,以及一段时间后自动收起的状态栏
    if (updateBarMode(barMode)) {
        // 同上,计算图标颜色
        mLightBarController.onStatusBarModeChanged(barMode);
        updateBubblesVisibility();
    }
}

当状态栏短暂出现时,通过不断的点击状态栏,可以重置状态栏自动收起的时间;
该部分逻辑是statusbar中注册了重置自动收起的点击事件

// CentralSurfacesImpl

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    ...
    mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
    ...
}
 
protected View.OnTouchListener getStatusBarWindowTouchListener() {
    return (v, event) -> {
        // here !!
        mAutoHideController.checkUserAutoHide(event);
        mRemoteInputManager.checkRemoteInputOutside(event);
        mShadeController.onStatusBarTouch(event);
        return mNotificationShadeWindowView.onTouchEvent(event);
    };
}
 
public void checkUserAutoHide(MotionEvent event) {
    ...
    if (shouldHide) {
        userAutoHide();
    }
}
 
private void userAutoHide() {
    cancelAutoHide();
    mHandler.postDelayed(mAutoHide, getUserAutoHideTimeout());
}

写在后面

如果文章中有错误的地方,希望各位大佬们批评指正~

If you like this article, it is written by Johnny Deng.
If not, I don’t know who wrote it.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值