SystemUI架构分析学习

目录

1.SystemUI路径及概念介绍

2.SystemUI主要功能

2.1Status bars详解

2.2Notification流程

2.3Navigation bars详解


1.SystemUI路径及概念介绍

源码路径:frameworks/base/packages/SystemUI/

安装路径:system/priv-app/-SystemUI

SystemUI是以apk的形势在Android系统中存在的

2.SystemUI主要功能

1.Status bars(状态栏):通知消息提示和状态展示

2.Notification(通知面板):展示系统或应用的通知内容,提供快速系统设置开关

3.Navigation bars(导航栏):返回,HOME,Recent

4.KeyGuard(键盘锁):锁屏模块

5.Recents 近期应用管理,以堆叠的形式展示

6.ScreenShot (截屏):长按电源键+音量下键后截屏,用以展示截取的屏幕照片/内容

7.VolumeUI 展示或控制音量的变化:媒体、铃音、通知、通话音量

8.PowerUI 主要处理和Power相关的事件

9.RingtonePlayer 铃音播放

10.StackDivider 控制管理分屏

2.1Status bars详解

启动分析

首选调用com.android.systemui.SystemBars的start方法

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java

      @Override
40    public void start() {
41        if (DEBUG) Log.d(TAG, "start");
42        createStatusBarFromConfig();
43    }
 
 
    private void createStatusBarFromConfig() {
53        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
          //取出className
54        final String clsName = mContext.getString(R.string.config_statusBarComponent);
55        if (clsName == null || clsName.length() == 0) {
56            throw andLog("No status bar component configured", null);
57        }
          // 通过反射获取该对象
58        Class cls = null;
59        try {
60            cls = mContext.getClassLoader().loadClass(clsName);
61        } catch (Throwable t) {
62            throw andLog("Error loading status bar component: " + clsName, t);
63        }
64        try {
65            mStatusBar = (SystemUI) cls.newInstance();
66        } catch (Throwable t) {
67            throw andLog("Error creating status bar component: " + clsName, t);
68        }
          //填充信息并启动 StatusBar  start() 方法
69        mStatusBar.mContext = mContext;
70        mStatusBar.mComponents = mComponents;
71        mStatusBar.start();
72        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
73    }

主要是通过反射获取config_statusBarComponent 中定义的对象,并启动该对象的start方法。config_statusBarComponent 的值有3种,默认是phone布局,另外两个是tv和car。

接下来是StautsBar中的start方法

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java 

着重看下start中的createAndAddWindows方法,在此之前先了解下状态栏的显示信息

状态栏显示

需要显示的信息分为以下5种:

  • 通知信息:在状态栏左侧显示一个图标提醒用户,并在下拉卷帘中为用户显示更加详细的信息。
  • 时间信息:显示在状态栏最右侧的一般小型数字时钟,是一个名为Clock的继承自TextView的控件。监听了几个和时间有关的广播:ACTION_TIME_TICK、ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_CONFIGURAITON_CHANGED。当其中一个广播到来时从Calendar类中获取当前的系统时间,然后进行字符串格式化后显示出来。时间信息的维护工作在状态栏内部完成,外界无法通过API修改时间信息的显示或行为。
  • 电量信息:显示一个电池图标,用于提示设备当前的电量情况。它是一个被BatteryController类所管理的ImageView。BatteryController通过监听android.intent.action.ABTTERY_CHANGED广播以从BatteryService中获取电量信息。和时间信息一样,外界无法干预状态栏对电量信息的显示行为。
  • 信号信息:显示系统当前的WiFi、移动信号网络的信号状态。用户所看到的WiFi图标、手机信号图标、飞行模式图标都属于信号信息的范围。它们被NetworkController类维护着。NetworkController监听了一系列与信号相关的广播,如WIFI_STATE_CHANGED_ACTION 、WIFI_STATE_SIM_ACTION、ACTION_AIRPLANE_MODE_CHANGED等,并在这些广播到来时显示、更改或移除相关的图标。注意,在Android 8.0之后,手机信号图标不在通过ImageView而是通过自定义view实现。
  • 系统状态图标区:这个区域用来显示系统当前的状态,比如可以展示蓝牙状态、闹铃等。StatusBarManagerService通过setIcon()接口为外界提供了修改系统状态图标的途径,但是它对信息的内容有很强的限制。1、系统状态图标无法显示图标以外的信息;2、系统状态图标对其显示的图标数量以及图标锁表示的意图有严格的限制。

继续了解createAndAddWindows 

    public void createAndAddWindows() {
        addStatusBarWindow();
    }
 
    private void addStatusBarWindow() {
	    //创建状态栏的控件树
        makeStatusBarView();
        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
        mRemoteInputController = new RemoteInputController(mHeadsUpManager);
		//通过StatusBarWindowManager.add创建状态栏的窗口
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }
	/*
	*获取statusbar高度,在framework/base/core/res/res/values/diamens.xml中设置
	*/
    public int getStatusBarHeight() {
        if (mNaturalBarHeight < 0) {
            final Resources res = mContext.getResources();
            mNaturalBarHeight =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        }
        return mNaturalBarHeight;
    }
	
    // ================================================================================
    // Constructing the view
    // ================================================================================
    protected void makeStatusBarView() {
        final Context context = mContext;
       //获取屏幕参数
        updateDisplaySize(); // populates mDisplayMetrics
       //更新Panels资源数据,statusbar包含很多panel,在创建PhoneStatusBarView时需要更新panel数据
        updateResources();
        updateTheme();
 
        inflateStatusBarWindow(context);  //加载布局
        mStatusBarWindow.setService(this);
        mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); //mStatusBarWindow的点击事件
        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                    mStatusBarView = (PhoneStatusBarView) fragment.getView();
                    mStatusBarView.setBar(this);
                    mStatusBarView.setPanel(mNotificationPanel);
                    mStatusBarView.setScrimController(mScrimController);
                    mStatusBarView.setBouncerShowing(mBouncerShowing);
                    setAreThereNotifications();
                    checkBarModes();
                }).getFragmentManager()
                .beginTransaction()
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)  //替换为CollapsedStatusBarFragment
                .commit();
        mIconController = Dependency.get(StatusBarIconController.class);
    }
	/*
	*加载布局
	*/
	protected void inflateStatusBarWindow(Context context) {
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
    }

 调用流程是createAndAddWindows——>addStatusBarWindow——>makeStatusBarView

    /**
87     * Adds the status bar view to the window manager.
88     *
89     * @param statusBarView The view to add.
90     * @param barHeight The height of the status bar in collapsed state.
91     */
92    public void add(View statusBarView, int barHeight) {
93
94        // Now that the status bar window encompasses the sliding panel and its
95        // translucent backdrop, the entire thing is made TRANSLUCENT and is
96        // hardware-accelerated.
97        mLp = new WindowManager.LayoutParams(
98                ViewGroup.LayoutParams.MATCH_PARENT,
99                barHeight,
100                WindowManager.LayoutParams.TYPE_STATUS_BAR,
101                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
102                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
103                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
104                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
105                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
106                PixelFormat.TRANSLUCENT);
107        mLp.token = new Binder();
108        mLp.gravity = Gravity.TOP;
109        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
110        mLp.setTitle("StatusBar");
111        mLp.packageName = mContext.getPackageName();
112        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
113        mStatusBarView = statusBarView;
114        mBarHeight = barHeight;
115        mWindowManager.addView(mStatusBarView, mLp);
116        mLpChanged = new WindowManager.LayoutParams();
117        mLpChanged.copyFrom(mLp);
118    }

状态栏的高度是从frameworks/base/core/res/res/values/dimens.xml中获取的,默认为25dp。

TYPE_STATUS_BAR使得PhomeWindowManager为状态栏的窗口分配了较大的layer值,使其可以显示在其它应用窗口上。

FLAG_NOT_FOCUSABLE、FLAG_TOUCHABLE_WHEN_WAKING、FLAG_SPLIT_TOUCH
定义了输入事件的响应行为。另外当窗口创建后LayoutParams是会反生变化的。状态栏窗口创建时高度为25dip,flags描述为其不可接受按键事件。不过当用户按下状态栏导致卷帘下拉时,StatusBar会通过WindowManager.updateViewLayout()方法修改窗口的LayoutParams高度为match_parent,即充满整个屏幕使得卷帘可以满屏显示,并且移除FLAG_NOT_FOCUSABLE,使得StatusBar可以监听back按钮

状态栏控件树结构

SystemUI架构分析学习_第1张图片

2.2Notification流程

不管是发出一个新的通知还是对已经存在的通知进行更新,调用的都是NotificationManager.notify(int id,Notification notification)。最后走到SystemUI的时候首先调用StatusBar中的成员变量mNotificationListener的onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap)方法。

public void onNotificationPosted(final StatusBarNotification sbn,
        final RankingMap rankingMap) {
    if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                processForRemoteInput(sbn.getNotification());
                String key = sbn.getKey();
                mKeysKeptForRemoteInput.remove(key);
                boolean isUpdate = mNotificationData.get(key) != null;
                ......
                    // Remove existing notification to avoid stale data.
                    if (isUpdate) {
                        removeNotification(key, rankingMap);
                    } else {
                        mNotificationData.updateRanking(rankingMap);
                    }
                    return;
                }
                try {
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);
                    } else {
                        addNotification(sbn, rankingMap);
                    }
                } catch (InflationException e) {
                    handleInflationException(sbn, e);
                }
            }
        });
    }
}

首先来看方法中的两个参数:1.StatusBarNotification sbn;2.RankingMap rankingMap。
StatusBarNotification点进去看,发现其实是由Notification组装而成,里面比较重要的属性有String pkg,int id,String key,Notification notification,保存着通知的内容,发出通知的报名信息,以及id等。StatusBarNotification 具体的组装生成过程不是在SystemUI包中进行,暂不关注。

RankingMap则是NotificationListenerService的一个静态内部类,里面保存着所有Notification相关的信息。

boolean isUpdate = mNotificationData.get(sbn.getKey()) != null;

mNotificationData是StatusBar的一个protected成员变量,可被子类继承,自己本身的类是NotificationData,位于SystemUI工程下的com.android.systemui.statusbar

mNotificationData.get(key) 返回了Entry对象,是NotificationData的一个内部类。其中包含的几个重要的属性的属性:

public String key;
public StatusBarNotification notification;
public NotificationChannel channel;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
public ExpandableNotificationRow row; // the outer expanded view

如果mNotificationData能通过sbn的key拿到的Entry不为空,说明这个通知已经存在了,isUpdate为true走更新流程,否则走添加流程。到此,onNotificationPosted方法就结束了

接着看添加流程addNotification(sbn, rankingMap)。

public void addNotification(StatusBarNotification notification, RankingMap ranking)
        throws InflationException {
    String key = notification.getKey();
    mNotificationData.updateRanking(ranking);
    Entry shadeEntry = createNotificationViews(notification);
    boolean isHeadsUped = shouldPeek(shadeEntry);
    ......
    abortExistingInflation(key);
 
    mForegroundServiceController.addNotification(notification,
            mNotificationData.getImportance(key));
 
    mPendingNotifications.put(key, shadeEntry);
}

首先通过传来的StatusBarNotification notification封装构造出一个Entry对象

Entry shadeEntry = createNotificationViews(notification);

跟过去看createNotificationViews(notification)方法,这里又跳回了StatusBar。

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
        throws InflationException {
    NotificationData.Entry entry = new NotificationData.Entry(sbn);
    Dependency.get(LeakDetector.class).trackInstance(entry);
    entry.createIcons(mContext, sbn);
    // Construct the expanded view.
    inflateViews(entry, mStackScroller);
    return entry;
}

inflateViews(entry, mStackScroller)。第二个参数mStackScroller,就是SystemUI中的下拉通知栏里面所有通知以及一些其他view的父view,是StatusBar中一个成员变量。

protected void inflateViews(Entry entry, ViewGroup parent) {
    PackageManager pmUser = getPackageManagerForUser(mContext,
            entry.notification.getUser().getIdentifier());
 
    final StatusBarNotification sbn = entry.notification;
    if (entry.row != null) {
        entry.reset();
        updateNotification(entry, pmUser, sbn, entry.row);
    } else {
        new RowInflaterTask().inflate(mContext, parent, entry,
                row -> {
                    bindRow(entry, pmUser, sbn, row);
                    updateNotification(entry, pmUser, sbn, row);
                });
    }
 
}

看RowInflaterTask().inflate方法,该方法在RowInflaterTask中 

public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
        RowInflationFinishedListener listener) {
    mListener = listener;
    AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
    mEntry = entry;
    entry.setInflationTask(this);
    inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

row(ExpandableNotificationRow)就是最终添加到通知栏上的通知对应的view,它的布局文件是R.layout.status_bar_notification_row。

AsyncLayoutInflater这个类是NotificationInflater的静态内部类,其中有方onAsyncInflationFinished如下 

public void onAsyncInflationFinished(NotificationData.Entry entry) {
    mRow.getEntry().onInflationTaskFinished();
    mRow.onNotificationUpdated();
    mCallback.onAsyncInflationFinished(mRow.getEntry());
}

 onAsyncInflationFinished的实现在StatusBar中

public void onAsyncInflationFinished(Entry entry) {
    mPendingNotifications.remove(entry.key);
    // If there was an async task started after the removal, we don't want to add it back to
    // the list, otherwise we might get leaks.
    boolean isNew = mNotificationData.get(entry.key) == null;
    if (isNew && !entry.row.isRemoved()) {
        addEntry(entry);//重点
    } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
        mVisualStabilityManager.onLowPriorityUpdated(entry);
        updateNotificationShade();
    }
    entry.row.setLowPriorityStateUpdated(false);
}

addEntry是重点,如下

private void addEntry(Entry shadeEntry) {
    boolean isHeadsUped = shouldPeek(shadeEntry);
    if (isHeadsUped) {
        mHeadsUpManager.showNotification(shadeEntry);
        // Mark as seen immediately
        setNotificationShown(shadeEntry.notification);
    }
    addNotificationViews(shadeEntry);//重点
    // Recalculate the position of the sliding windows and the titles.
    setAreThereNotifications();
}

addNotificationViews的内容如下

protected void addNotificationViews(Entry entry) {
    if (entry == null) {
        return;
    }
    // Add the expanded view and icon.
    mNotificationData.add(entry);
    updateNotifications();
}

mNotificationData.add(entry);对应了前面分析的boolean isUpdate = mNotificationData.get(key) != null;

进入updateNotifications()方法,内容如下

protected void updateNotifications() {
    mNotificationData.filterAndSort();
 
    updateNotificationShade();
}

重点在updateNotificationShade()(可以在这个方法里进行修改达到想要的需求) 

private void updateNotificationShade() {
    if (mStackScroller == null) return;
 
    // Do not modify the notifications during collapse.
    if (isCollapsing()) {
        addPostCollapseAction(new Runnable() {
            @Override
            public void run() {
                updateNotificationShade();
            }
        });
        return;
    }
 
    ArrayList activeNotifications = mNotificationData.getActiveNotifications();
    ArrayList toShow = new ArrayList<>(activeNotifications.size());
    final int N = activeNotifications.size();
    for (int i=0; i orderedChildren =
                    mTmpChildOrderMap.get(summary);
            if (orderedChildren == null) {
                orderedChildren = new ArrayList<>();
                mTmpChildOrderMap.put(summary, orderedChildren);
            }
            orderedChildren.add(ent.row);
        } else {
            toShow.add(ent.row);
        }
 
    }
 
    ArrayList toRemove = new ArrayList<>();
    for (int i=0; i< mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
            toRemove.add((ExpandableNotificationRow) child);
        }
    }
 
    for (ExpandableNotificationRow remove : toRemove) {
        if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
            // we are only transfering this notification to its parent, don't generate an animation
            mStackScroller.setChildTransferInProgress(true);
        }
        if (remove.isSummaryWithChildren()) {
            remove.removeAllChildren();
        }
        mStackScroller.removeView(remove);
        mStackScroller.setChildTransferInProgress(false);
    }
 
    removeNotificationChildren();
 
    for (int i=0; i

显示做mStackScroller的空判断,然后是通知栏动画状态的判断。一切OK,就:
1.mNotificationData获取数据Entry集合,构造一个大小和这个Entry集合一样的ExpandableNotificationRow集合toShow
2.遍历entry,把entry.row添加到toshow里面
3.原有的通知,但是toshow里没有的则移除,然后toshow里没添加上的添加上去

bindRow(entry, pmUser, sbn, row)方法

private void bindRow(Entry entry, PackageManager pmUser,
        StatusBarNotification sbn, ExpandableNotificationRow row) {
    row.setExpansionLogger(this, entry.notification.getKey());
    row.setGroupManager(mGroupManager);
    row.setHeadsUpManager(mHeadsUpManager);
    row.setAboveShelfChangedListener(mAboveShelfObserver);
    row.setRemoteInputController(mRemoteInputController);
    row.setOnExpandClickListener(this);
    row.setRemoteViewClickHandler(mOnClickHandler);
    row.setInflationCallback(this);
    row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
    final String pkg = sbn.getPackageName();
    String appname = pkg;
    try {
        final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
                PackageManager.MATCH_UNINSTALLED_PACKAGES
                        | PackageManager.MATCH_DISABLED_COMPONENTS);
        if (info != null) {
            appname = String.valueOf(pmUser.getApplicationLabel(info));
        }
    } catch (NameNotFoundException e) {
        // Do nothing
    }
    row.setAppName(appname);
    row.setOnDismissRunnable(() ->
            performRemoveNotification(row.getStatusBarNotification()));
    row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
//该句实现了焦点的策略,row(也就是我们的通知)有子控件需要焦点则把焦点交给子控件,否则给row。
    if (ENABLE_REMOTE_INPUT) {
        row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
    }
}

updateNotification(entry,pmUser,sbn,entry.row)方法

private void updateNotification(Entry entry, PackageManager pmUser,
        StatusBarNotification sbn, ExpandableNotificationRow row) {
    row.setNeedsRedaction(needsRedaction(entry));
    boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
    boolean isUpdate = mNotificationData.get(entry.key) != null;
    boolean wasLowPriority = row.isLowPriority();
    row.setIsLowPriority(isLowPriority);
    row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
    // bind the click event to the content area
    mNotificationClicker.register(row, sbn);//给通知栏注册点击事件
 
    // Extract target SDK version.
    try {
        ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
        entry.targetSdk = info.targetSdkVersion;
    } catch (NameNotFoundException ex) {
        Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
    }
    row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
            && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
    entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
    entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
    entry.row = row;
    entry.row.setOnActivatedListener(this);
 
    boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
            mNotificationData.getImportance(sbn.getKey()));
    boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
    row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
    row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
    row.updateNotification(entry);
}

mNotificationClicker.register(row, sbn);mNotificationClicker是StatusBar的一个私有成员,对应的类是NotificationClicker,是一个StatusBar的内部类,实现了View.OnClickListener接口,它就是row的监听类,实现的功能是row被点击时,启动相应的pendingIntent.注册操作代码如下

public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
    Notification notification = sbn.getNotification();
    if (notification.contentIntent != null || notification.fullScreenIntent != null) {
        row.setOnClickListener(this);
    } else {
        row.setOnClickListener(null);
    }
}

进入ExpandableNotificationRow的updateNotification方法

public void updateNotification(NotificationData.Entry entry) {
    mEntry = entry;
    mStatusBarNotification = entry.notification;
    mNotificationInflater.inflateNotificationViews();
}

至此inflateViews()结束,Entry生成完毕,Entry中的row生成完毕。

移除通知 

首先还是StatusBar中的mNotificationListener但是和notification的添加/更新不同的是,走的不再是onNotificationPosted方法,而是onNotificationRemoved

public void onNotificationRemoved(StatusBarNotification sbn,
        final RankingMap rankingMap) {
    if (true/**DEBUG*/) Log.d(TAG, "onNotificationRemoved: " + sbn);
    if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
        final String key = sbn.getKey();
        mHandler.post(() -> removeNotification(key, rankingMap));
    }
}

这个方法就是很简单地拿个key,然后走removeNotification(key, rankingMap)方法

public void removeNotification(String key, RankingMap ranking) {
    boolean deferRemoval = false;
    abortExistingInflation(key);
    if (mHeadsUpManager.isHeadsUp(key)) {
        // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
        // sending look longer than it takes.
        // Also we should not defer the removal if reordering isn't allowed since otherwise
        // some notifications can't disappear before the panel is closed.
        boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key)
                && !FORCE_REMOTE_INPUT_HISTORY
                || !mVisualStabilityManager.isReorderingAllowed();
        deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
    }
    if (key.equals(mMediaNotificationKey)) {
        clearCurrentMediaNotification();
        updateMediaMetaData(true, true);
    }
    if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
        Entry entry = mNotificationData.get(key);
        StatusBarNotification sbn = entry.notification;
 
        Notification.Builder b = Notification.Builder
                .recoverBuilder(mContext, sbn.getNotification().clone());
        CharSequence[] oldHistory = sbn.getNotification().extras
                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
        CharSequence[] newHistory;
        if (oldHistory == null) {
            newHistory = new CharSequence[1];
        } else {
            newHistory = new CharSequence[oldHistory.length + 1];
            for (int i = 0; i < oldHistory.length; i++) {
                newHistory[i + 1] = oldHistory[i];
            }
        }
        newHistory[0] = String.valueOf(entry.remoteInputText);
        b.setRemoteInputHistory(newHistory);
 
        Notification newNotification = b.build();
 
        // Undo any compatibility view inflation
        newNotification.contentView = sbn.getNotification().contentView;
        newNotification.bigContentView = sbn.getNotification().bigContentView;
        newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
 
        StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
                sbn.getOpPkg(),
                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
        boolean updated = false;
        try {
            updateNotification(newSbn, null);
            updated = true;
        } catch (InflationException e) {
            deferRemoval = false;
        }
        if (updated) {
            mKeysKeptForRemoteInput.add(entry.key);
            return;
        }
    }
    if (deferRemoval) {
        mLatestRankingMap = ranking;
        mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
        return;
    }
    Entry entry = mNotificationData.get(key);
 
    if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
            && (entry.row != null && !entry.row.isDismissed())) {
        mLatestRankingMap = ranking;
        mRemoteInputEntriesToRemoveOnCollapse.add(entry);
        return;
    }
    if (entry != null && mNotificationGutsExposed != null
            && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
            && !entry.row.getGuts().isLeavebehind()) {
        Log.w(TAG, "Keeping notification because it's showing guts. " + key);
        mLatestRankingMap = ranking;
        mKeyToRemoveOnGutsClosed = key;
        return;
    }
 
    if (entry != null) {
        mForegroundServiceController.removeNotification(entry.notification);
    }
 
    if (entry != null && entry.row != null) {
        entry.row.setRemoved();
        mStackScroller.cleanUpViewState(entry.row);
    }
    // Let's remove the children if this was a summary
    handleGroupSummaryRemoved(key, ranking);
    StatusBarNotification old = removeNotificationViews(key, ranking);
    /// M: Enable this log for unusual case debug.
    if (true/**SPEW*/) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
    if (old != null) {
        if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
                && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
            if (mState == StatusBarState.SHADE) {
                animateCollapsePanels();
            } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
                goToKeyguard();
            }
        }
    }
    setAreThereNotifications();
}

 removeNotificationViews(key, ranking)方法是在StatusBar中定义的

protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
    NotificationData.Entry entry = mNotificationData.remove(key, ranking);
    if (entry == null) {
        Log.w(TAG, "removeNotification for unknown key: " + key);
        return null;
    }
    updateNotifications();
    Dependency.get(LeakDetector.class).trackGarbage(entry);
    return entry.notification;
}

里面逻辑也很简单,根据key,从mNotificationData移除entry,然后就是走回updateNotifications()刷新UI。 

2.3Navigation bars详解

导航栏的创建

StatusBar.makeStatusBarView

 try {
      //创建导航栏
      boolean showNav = mWindowManagerService.hasNavigationBar();
      if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
          if (showNav) {
             createNavigationBar();
          }
 } catch (RemoteException ex) {
      // no window manager? good luck with that
 }
 protected void createNavigationBar() {
        mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
            mNavigationBar = (NavigationBarFragment) fragment;
            if (mLightBarController != null) {
                mNavigationBar.setLightBarController(mLightBarController);
            }
            mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
        });
    }

NavigationBarFragment.create

public static View create(Context context, FragmentListener listener) {
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                PixelFormat.TRANSLUCENT);
        lp.token = new Binder();
        lp.setTitle("NavigationBar");
        lp.accessibilityTitle = context.getString(R.string.nav_bar);
        lp.windowAnimations = 0;
 
        View navigationBarView = LayoutInflater.from(context).inflate(
                R.layout.navigation_bar_window, null);
 
        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
        if (navigationBarView == null) return null;
 
        context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
        FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
        NavigationBarFragment fragment = new NavigationBarFragment();
        fragmentHost.getFragmentManager().beginTransaction()
                .replace(R.id.navigation_bar_frame, fragment, TAG)
                .commit();
        fragmentHost.addTagListener(TAG, listener);
        return navigationBarView;
    }

上述代码做了两件事:

1.创建navigationBarView 并且把navigationBarView添加到windowManager中。

2.创建NavigationBarFragment 替换navigation_bar_window的布局文件,改成navigation_bar

navigation_bar.xml


 
    
 

NavigationBarView 完成资源文件添加

NavigationBarFragment  添加点击事件和触摸事件的处理逻辑 

NavigationBarFragment

  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mNavigationBarView = (NavigationBarView) view;
 
        mNavigationBarView.setDisabledFlags(mDisabledFlags1);
        mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());
        mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
        mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
        if (savedInstanceState != null) {
            mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
        }
 
        prepareNavigationBarView();//添加home  ,recent触摸事件回调
        checkNavBarModes();
 
        setDisabled2Flags(mDisabledFlags2);
 
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
        notifyNavigationBarScreenOn();
        mOverviewProxyService.addCallback(mOverviewProxyListener);
    }
 private void prepareNavigationBarView() {
        mNavigationBarView.reorient();
     //近期列表安静控制
        ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
        recentsButton.setOnClickListener(this::onRecentsClick);
        recentsButton.setOnTouchListener(this::onRecentsTouch);
        recentsButton.setLongClickable(true);
        recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
 
        ButtonDispatcher backButton = mNavigationBarView.getBackButton();
        backButton.setLongClickable(true);
 
        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
        homeButton.setOnTouchListener(this::onHomeTouch);
        homeButton.setOnLongClickListener(this::onHomeLongClick);
 
        ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
        accessibilityButton.setOnClickListener(this::onAccessibilityClick);
        accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
        updateAccessibilityServicesState(mAccessibilityManager);
 
        ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
        rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
        rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
        updateScreenPinningGestures();
    }

 

 

 

 

 

你可能感兴趣的:(android)