目录
1.SystemUI路径及概念介绍
2.SystemUI主要功能
2.1Status bars详解
2.2Notification流程
源码路径:frameworks/base/packages/SystemUI/
安装路径:system/priv-app/-SystemUI
SystemUI是以apk的形势在Android系统中存在的
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 控制管理分屏
启动分析
首选调用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种:
继续了解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按钮
状态栏控件树结构
不管是发出一个新的通知还是对已经存在的通知进行更新,调用的都是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。
导航栏的创建
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();
}