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按钮
状态栏控件树结构
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() {@Overridepublic 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 immediatelysetNotificationShown(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() {@Overridepublic void run() {updateNotificationShade();}});return;}ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());final int N = activeNotifications.size();for (int i=0; i<N; i++) {Entry ent = activeNotifications.get(i);if (ent.row.isDismissed() || ent.row.isRemoved()) {// we don't want to update removed notifications because they could// temporarily become children if they were isolated before.continue;}int userId = ent.notification.getUserId();// Display public version of the notification if we need to redact.boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);boolean userPublic = devicePublic || isLockscreenPublicMode(userId);boolean needsRedaction = needsRedaction(ent);boolean sensitive = userPublic && needsRedaction;boolean deviceSensitive = devicePublic&& !userAllowsPrivateNotificationsInPublic(mCurrentUserId);ent.row.setSensitive(sensitive, deviceSensitive);ent.row.setNeedsRedaction(needsRedaction);if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {ExpandableNotificationRow summary = mGroupManager.getGroupSummary(ent.row.getStatusBarNotification());List<ExpandableNotificationRow> orderedChildren =mTmpChildOrderMap.get(summary);if (orderedChildren == null) {orderedChildren = new ArrayList<>();mTmpChildOrderMap.put(summary, orderedChildren);}orderedChildren.add(ent.row);} else {toShow.add(ent.row);}}ArrayList<ExpandableNotificationRow> 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 animationmStackScroller.setChildTransferInProgress(true);}if (remove.isSummaryWithChildren()) {remove.removeAllChildren();}mStackScroller.removeView(remove);mStackScroller.setChildTransferInProgress(false);}removeNotificationChildren();for (int i=0; i<toShow.size(); i++) {View v = toShow.get(i);if (v.getParent() == null) {//*/ freeme.biantao, 20180207. Theme.Freeme 8.1 - notification.if (v instanceof ExpandableNotificationRow) {ExpandableNotificationRow row = (ExpandableNotificationRow) v;if (row.isStyleInGroup()) {row.setStyleInGroup(false);}}//*/mVisualStabilityManager.notifyViewAddition(v);mStackScroller.addView(v);}}addNotificationChildrenAndSort();// So after all this work notifications still aren't sorted correctly.// Let's do that now by advancing through toShow and mStackScroller in// lock-step, making sure mStackScroller matches what we see in toShow.int j = 0;for (int i = 0; i < mStackScroller.getChildCount(); i++) {View child = mStackScroller.getChildAt(i);if (!(child instanceof ExpandableNotificationRow)) {// We don't care about non-notification views.continue;}ExpandableNotificationRow targetChild = toShow.get(j);if (child != targetChild) {// Oops, wrong notification at this position. Put the right one// here and advance both lists.if (mVisualStabilityManager.canReorderNotification(targetChild)) {mStackScroller.changeViewPosition(targetChild, i);} else {mVisualStabilityManager.addReorderingAllowedCallback(this);}}j++;}mVisualStabilityManager.onReorderingFinished();// clear the map again for the next usagemTmpChildOrderMap.clear();updateRowStates();updateSpeedBumpIndex();updateClearAll();updateEmptyShadeView();updateQsExpansionEnabled();// Let's also update the iconsmNotificationIconAreaController.updateNotificationIcons(mNotificationData);
}
显示做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 areamNotificationClicker.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 inflationnewNotification.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 summaryhandleGroupSummaryRemoved(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
<com.android.systemui.statusbar.phone.NavigationBarViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:systemui="http://schemas.android.com/apk/res-auto"android:layout_height="match_parent"android:layout_width="match_parent"android:background="@drawable/system_bar_background"><com.android.systemui.statusbar.phone.NavigationBarInflaterViewandroid:id="@+id/navigation_inflater"android:layout_width="match_parent"android:layout_height="match_parent" /></com.android.systemui.statusbar.phone.NavigationBarView>
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();}
SystemUI架构分析学习相关推荐
- Android SystemUI 架构详解
Android SystemUI 架构详解 本文描述Android系统中一个核心应用SystemUI,详细赘述SystemUI中几大模块功能的实现过程.由于作者水平有限,如发现本文中错误的地方,欢迎指 ...
- SystemUI架构分析
SystemUI架构分析 SystemUI架构分析 前言 1SystemUI介绍 1SystemUI摘要 2什么是SystemUI 2SystemUI的启动过程 3SystemUI的SERVICES ...
- SystemUI架构设计
目录 1.什么是SystemUI 2.SystemUI的模块划分 3.SystemUI架构图 SystemUI结构图 SystemUI类图 SystemUI启动流程 4.通知 通知所在的位置 通知的注 ...
- Android自定义快速设置
Android自定义快速设置 Customizer Quick Setting 自定义快速设置 Android N/7.0 牛轧糖 Android自定义快速设置 前言 自定义快速设置图块 深入理解Ti ...
- [深入SystemUI]-了解SystemUI的大致架构
文章目录 1. SystemUI的架构图 2. 对我的架构图的解释 2.1 为什么要将SystemUIService和SystemUIApplication放在一层? 2.2 抽象服务层 2.3 功能 ...
- 大型网站技术架构核心原理与案例分析学习笔记(理论篇)
大型网站软件系统的特点 高并发.大流量 高可用 海量数据 用户分布广泛,网络情况复杂 安全环境恶劣 需求快速变更,发布频繁 渐进式发展 大型网站架构演化发展历程 所有资源在一台服务器上,使用免费开源的 ...
- 大型网站技术架构核心原理与案例分析学习笔记(实践篇)
Wikipedia性能优化策略 前端性能优化 80%以上的用户请求可以通过前端服务返回 CDN缓存的几条准则: 内容页面不包含动态信息,以免页面内容缓存很快失效或者包含过时信息 每个内容页面有唯一的R ...
- 软件需求分析学习日记(一)需求工程概述
软件需求分析学习日记(一)需求工程概述 文章目录 软件需求分析学习日记(一)需求工程概述 1.1需求工程的重要性 1.1.1几点说明和描述 1.1.2不适当的需求过程所引起的风险 1.2什么是软件需求 ...
- Serverless 风格微服务的持续交付(上):架构案例
GitChat 作者:顾宇 原文:Serverless 风格微服务的持续交付(上):架构案例 关注微信公众号:GitChat 技术杂谈 ,一本正经的讲技术 [不要错过文末彩蛋] 无服务器架构 (Ser ...
最新文章
- OVH数据中心失火事件关于运维管理的思考
- 最新章节 第238章 超级计算机的安排,第238章 谁敢动我们十八里铺的人?
- 热力图怎么做_LncRNA这么热,5分左右的LncRNA研究文章应该怎么做
- 基于FFmpeg接收RTSP的ts流
- linux 使用 ioctl 参数
- 餐厅扫码点餐系统源码 外卖订餐小程序源码
- 怎么利用matlab求导,利用Matlab求导的几个命令
- xbox手柄接收器驱动_Xbox精英手柄,对这款游戏手柄使用感受
- “龙书”作者Jeffery Ullman:相信你自己,自由地思考
- apache ii评分怎么评_APACHE II评分表
- Go语言开发学习笔记(持续更新中)
- 计算机游戏物体运动代码,Flash游戏教程:控制物体运动三法 -电脑资料
- 【信息学奥赛一本通】2075:【21CSPJ普及组】插入排序(sort)
- MicroPython-On-ESP8266——数码管的使用,2片HC595驱动四位数码管
- 直方图均衡化、自适应直方图均衡化
- 软件测试流程-全程软件测试
- python导出dxf图,使用Python操作CAD的dxf文件,批量绘制变形图的方法记录
- 网络终极卫士 NetworkKiller V1.5
- 垃圾分类主题班会PPT模板
- 用手机怎么把图片转换成PDF