当前位置:   article > 正文

SystemUI架构分析学习

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

  1. @Override
  2. 40 public void start() {
  3. 41 if (DEBUG) Log.d(TAG, "start");
  4. 42 createStatusBarFromConfig();
  5. 43 }
  6. private void createStatusBarFromConfig() {
  7. 53 if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
  8. //取出className
  9. 54 final String clsName = mContext.getString(R.string.config_statusBarComponent);
  10. 55 if (clsName == null || clsName.length() == 0) {
  11. 56 throw andLog("No status bar component configured", null);
  12. 57 }
  13. // 通过反射获取该对象
  14. 58 Class<?> cls = null;
  15. 59 try {
  16. 60 cls = mContext.getClassLoader().loadClass(clsName);
  17. 61 } catch (Throwable t) {
  18. 62 throw andLog("Error loading status bar component: " + clsName, t);
  19. 63 }
  20. 64 try {
  21. 65 mStatusBar = (SystemUI) cls.newInstance();
  22. 66 } catch (Throwable t) {
  23. 67 throw andLog("Error creating status bar component: " + clsName, t);
  24. 68 }
  25. //填充信息并启动 StatusBar start() 方法
  26. 69 mStatusBar.mContext = mContext;
  27. 70 mStatusBar.mComponents = mComponents;
  28. 71 mStatusBar.start();
  29. 72 if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
  30. 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 

  1. public void createAndAddWindows() {
  2. addStatusBarWindow();
  3. }
  4. private void addStatusBarWindow() {
  5. //创建状态栏的控件树
  6. makeStatusBarView();
  7. mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
  8. mRemoteInputController = new RemoteInputController(mHeadsUpManager);
  9. //通过StatusBarWindowManager.add创建状态栏的窗口
  10. mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
  11. }
  12. /*
  13. *获取statusbar高度,在framework/base/core/res/res/values/diamens.xml中设置
  14. */
  15. public int getStatusBarHeight() {
  16. if (mNaturalBarHeight < 0) {
  17. final Resources res = mContext.getResources();
  18. mNaturalBarHeight =
  19. res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
  20. }
  21. return mNaturalBarHeight;
  22. }
  23. // ================================================================================
  24. // Constructing the view
  25. // ================================================================================
  26. protected void makeStatusBarView() {
  27. final Context context = mContext;
  28. //获取屏幕参数
  29. updateDisplaySize(); // populates mDisplayMetrics
  30. //更新Panels资源数据,statusbar包含很多panel,在创建PhoneStatusBarView时需要更新panel数据
  31. updateResources();
  32. updateTheme();
  33. inflateStatusBarWindow(context); //加载布局
  34. mStatusBarWindow.setService(this);
  35. mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); //mStatusBarWindow的点击事件
  36. Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
  37. FragmentHostManager.get(mStatusBarWindow)
  38. .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
  39. CollapsedStatusBarFragment statusBarFragment =
  40. (CollapsedStatusBarFragment) fragment;
  41. statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
  42. mStatusBarView = (PhoneStatusBarView) fragment.getView();
  43. mStatusBarView.setBar(this);
  44. mStatusBarView.setPanel(mNotificationPanel);
  45. mStatusBarView.setScrimController(mScrimController);
  46. mStatusBarView.setBouncerShowing(mBouncerShowing);
  47. setAreThereNotifications();
  48. checkBarModes();
  49. }).getFragmentManager()
  50. .beginTransaction()
  51. .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
  52. CollapsedStatusBarFragment.TAG) //替换为CollapsedStatusBarFragment
  53. .commit();
  54. mIconController = Dependency.get(StatusBarIconController.class);
  55. }
  56. /*
  57. *加载布局
  58. */
  59. protected void inflateStatusBarWindow(Context context) {
  60. mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
  61. R.layout.super_status_bar, null);
  62. }

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

  1. /**
  2. 87 * Adds the status bar view to the window manager.
  3. 88 *
  4. 89 * @param statusBarView The view to add.
  5. 90 * @param barHeight The height of the status bar in collapsed state.
  6. 91 */
  7. 92 public void add(View statusBarView, int barHeight) {
  8. 93
  9. 94 // Now that the status bar window encompasses the sliding panel and its
  10. 95 // translucent backdrop, the entire thing is made TRANSLUCENT and is
  11. 96 // hardware-accelerated.
  12. 97 mLp = new WindowManager.LayoutParams(
  13. 98 ViewGroup.LayoutParams.MATCH_PARENT,
  14. 99 barHeight,
  15. 100 WindowManager.LayoutParams.TYPE_STATUS_BAR,
  16. 101 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  17. 102 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  18. 103 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
  19. 104 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
  20. 105 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
  21. 106 PixelFormat.TRANSLUCENT);
  22. 107 mLp.token = new Binder();
  23. 108 mLp.gravity = Gravity.TOP;
  24. 109 mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
  25. 110 mLp.setTitle("StatusBar");
  26. 111 mLp.packageName = mContext.getPackageName();
  27. 112 mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
  28. 113 mStatusBarView = statusBarView;
  29. 114 mBarHeight = barHeight;
  30. 115 mWindowManager.addView(mStatusBarView, mLp);
  31. 116 mLpChanged = new WindowManager.LayoutParams();
  32. 117 mLpChanged.copyFrom(mLp);
  33. 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)方法。

  1. public void onNotificationPosted(final StatusBarNotification sbn,
  2. final RankingMap rankingMap) {
  3. if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
  4. mHandler.post(new Runnable() {
  5. @Override
  6. public void run() {
  7. processForRemoteInput(sbn.getNotification());
  8. String key = sbn.getKey();
  9. mKeysKeptForRemoteInput.remove(key);
  10. boolean isUpdate = mNotificationData.get(key) != null;
  11. ......
  12. // Remove existing notification to avoid stale data.
  13. if (isUpdate) {
  14. removeNotification(key, rankingMap);
  15. } else {
  16. mNotificationData.updateRanking(rankingMap);
  17. }
  18. return;
  19. }
  20. try {
  21. if (isUpdate) {
  22. updateNotification(sbn, rankingMap);
  23. } else {
  24. addNotification(sbn, rankingMap);
  25. }
  26. } catch (InflationException e) {
  27. handleInflationException(sbn, e);
  28. }
  29. }
  30. });
  31. }
  32. }

首先来看方法中的两个参数: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的一个内部类。其中包含的几个重要的属性的属性:

  1. public String key;
  2. public StatusBarNotification notification;
  3. public NotificationChannel channel;
  4. public StatusBarIconView icon;
  5. public StatusBarIconView expandedIcon;
  6. public ExpandableNotificationRow row; // the outer expanded view

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

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

  1. public void addNotification(StatusBarNotification notification, RankingMap ranking)
  2. throws InflationException {
  3. String key = notification.getKey();
  4. mNotificationData.updateRanking(ranking);
  5. Entry shadeEntry = createNotificationViews(notification);
  6. boolean isHeadsUped = shouldPeek(shadeEntry);
  7. ......
  8. abortExistingInflation(key);
  9. mForegroundServiceController.addNotification(notification,
  10. mNotificationData.getImportance(key));
  11. mPendingNotifications.put(key, shadeEntry);
  12. }

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

Entry shadeEntry = createNotificationViews(notification);

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

  1. protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
  2. throws InflationException {
  3. NotificationData.Entry entry = new NotificationData.Entry(sbn);
  4. Dependency.get(LeakDetector.class).trackInstance(entry);
  5. entry.createIcons(mContext, sbn);
  6. // Construct the expanded view.
  7. inflateViews(entry, mStackScroller);
  8. return entry;
  9. }

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

  1. protected void inflateViews(Entry entry, ViewGroup parent) {
  2. PackageManager pmUser = getPackageManagerForUser(mContext,
  3. entry.notification.getUser().getIdentifier());
  4. final StatusBarNotification sbn = entry.notification;
  5. if (entry.row != null) {
  6. entry.reset();
  7. updateNotification(entry, pmUser, sbn, entry.row);
  8. } else {
  9. new RowInflaterTask().inflate(mContext, parent, entry,
  10. row -> {
  11. bindRow(entry, pmUser, sbn, row);
  12. updateNotification(entry, pmUser, sbn, row);
  13. });
  14. }
  15. }

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

  1. public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
  2. RowInflationFinishedListener listener) {
  3. mListener = listener;
  4. AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
  5. mEntry = entry;
  6. entry.setInflationTask(this);
  7. inflater.inflate(R.layout.status_bar_notification_row, parent, this);
  8. }

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

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

  1. public void onAsyncInflationFinished(NotificationData.Entry entry) {
  2. mRow.getEntry().onInflationTaskFinished();
  3. mRow.onNotificationUpdated();
  4. mCallback.onAsyncInflationFinished(mRow.getEntry());
  5. }

 onAsyncInflationFinished的实现在StatusBar中

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

addEntry是重点,如下

  1. private void addEntry(Entry shadeEntry) {
  2. boolean isHeadsUped = shouldPeek(shadeEntry);
  3. if (isHeadsUped) {
  4. mHeadsUpManager.showNotification(shadeEntry);
  5. // Mark as seen immediately
  6. setNotificationShown(shadeEntry.notification);
  7. }
  8. addNotificationViews(shadeEntry);//重点
  9. // Recalculate the position of the sliding windows and the titles.
  10. setAreThereNotifications();
  11. }

addNotificationViews的内容如下

  1. protected void addNotificationViews(Entry entry) {
  2. if (entry == null) {
  3. return;
  4. }
  5. // Add the expanded view and icon.
  6. mNotificationData.add(entry);
  7. updateNotifications();
  8. }

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

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

  1. protected void updateNotifications() {
  2. mNotificationData.filterAndSort();
  3. updateNotificationShade();
  4. }

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

  1. private void updateNotificationShade() {
  2. if (mStackScroller == null) return;
  3. // Do not modify the notifications during collapse.
  4. if (isCollapsing()) {
  5. addPostCollapseAction(new Runnable() {
  6. @Override
  7. public void run() {
  8. updateNotificationShade();
  9. }
  10. });
  11. return;
  12. }
  13. ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
  14. ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
  15. final int N = activeNotifications.size();
  16. for (int i=0; i<N; i++) {
  17. Entry ent = activeNotifications.get(i);
  18. if (ent.row.isDismissed() || ent.row.isRemoved()) {
  19. // we don't want to update removed notifications because they could
  20. // temporarily become children if they were isolated before.
  21. continue;
  22. }
  23. int userId = ent.notification.getUserId();
  24. // Display public version of the notification if we need to redact.
  25. boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
  26. boolean userPublic = devicePublic || isLockscreenPublicMode(userId);
  27. boolean needsRedaction = needsRedaction(ent);
  28. boolean sensitive = userPublic && needsRedaction;
  29. boolean deviceSensitive = devicePublic
  30. && !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
  31. ent.row.setSensitive(sensitive, deviceSensitive);
  32. ent.row.setNeedsRedaction(needsRedaction);
  33. if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
  34. ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
  35. ent.row.getStatusBarNotification());
  36. List<ExpandableNotificationRow> orderedChildren =
  37. mTmpChildOrderMap.get(summary);
  38. if (orderedChildren == null) {
  39. orderedChildren = new ArrayList<>();
  40. mTmpChildOrderMap.put(summary, orderedChildren);
  41. }
  42. orderedChildren.add(ent.row);
  43. } else {
  44. toShow.add(ent.row);
  45. }
  46. }
  47. ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
  48. for (int i=0; i< mStackScroller.getChildCount(); i++) {
  49. View child = mStackScroller.getChildAt(i);
  50. if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
  51. toRemove.add((ExpandableNotificationRow) child);
  52. }
  53. }
  54. for (ExpandableNotificationRow remove : toRemove) {
  55. if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
  56. // we are only transfering this notification to its parent, don't generate an animation
  57. mStackScroller.setChildTransferInProgress(true);
  58. }
  59. if (remove.isSummaryWithChildren()) {
  60. remove.removeAllChildren();
  61. }
  62. mStackScroller.removeView(remove);
  63. mStackScroller.setChildTransferInProgress(false);
  64. }
  65. removeNotificationChildren();
  66. for (int i=0; i<toShow.size(); i++) {
  67. View v = toShow.get(i);
  68. if (v.getParent() == null) {
  69. //*/ freeme.biantao, 20180207. Theme.Freeme 8.1 - notification.
  70. if (v instanceof ExpandableNotificationRow) {
  71. ExpandableNotificationRow row = (ExpandableNotificationRow) v;
  72. if (row.isStyleInGroup()) {
  73. row.setStyleInGroup(false);
  74. }
  75. }
  76. //*/
  77. mVisualStabilityManager.notifyViewAddition(v);
  78. mStackScroller.addView(v);
  79. }
  80. }
  81. addNotificationChildrenAndSort();
  82. // So after all this work notifications still aren't sorted correctly.
  83. // Let's do that now by advancing through toShow and mStackScroller in
  84. // lock-step, making sure mStackScroller matches what we see in toShow.
  85. int j = 0;
  86. for (int i = 0; i < mStackScroller.getChildCount(); i++) {
  87. View child = mStackScroller.getChildAt(i);
  88. if (!(child instanceof ExpandableNotificationRow)) {
  89. // We don't care about non-notification views.
  90. continue;
  91. }
  92. ExpandableNotificationRow targetChild = toShow.get(j);
  93. if (child != targetChild) {
  94. // Oops, wrong notification at this position. Put the right one
  95. // here and advance both lists.
  96. if (mVisualStabilityManager.canReorderNotification(targetChild)) {
  97. mStackScroller.changeViewPosition(targetChild, i);
  98. } else {
  99. mVisualStabilityManager.addReorderingAllowedCallback(this);
  100. }
  101. }
  102. j++;
  103. }
  104. mVisualStabilityManager.onReorderingFinished();
  105. // clear the map again for the next usage
  106. mTmpChildOrderMap.clear();
  107. updateRowStates();
  108. updateSpeedBumpIndex();
  109. updateClearAll();
  110. updateEmptyShadeView();
  111. updateQsExpansionEnabled();
  112. // Let's also update the icons
  113. mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
  114. }

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

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

  1. private void bindRow(Entry entry, PackageManager pmUser,
  2. StatusBarNotification sbn, ExpandableNotificationRow row) {
  3. row.setExpansionLogger(this, entry.notification.getKey());
  4. row.setGroupManager(mGroupManager);
  5. row.setHeadsUpManager(mHeadsUpManager);
  6. row.setAboveShelfChangedListener(mAboveShelfObserver);
  7. row.setRemoteInputController(mRemoteInputController);
  8. row.setOnExpandClickListener(this);
  9. row.setRemoteViewClickHandler(mOnClickHandler);
  10. row.setInflationCallback(this);
  11. row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
  12. final String pkg = sbn.getPackageName();
  13. String appname = pkg;
  14. try {
  15. final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
  16. PackageManager.MATCH_UNINSTALLED_PACKAGES
  17. | PackageManager.MATCH_DISABLED_COMPONENTS);
  18. if (info != null) {
  19. appname = String.valueOf(pmUser.getApplicationLabel(info));
  20. }
  21. } catch (NameNotFoundException e) {
  22. // Do nothing
  23. }
  24. row.setAppName(appname);
  25. row.setOnDismissRunnable(() ->
  26. performRemoveNotification(row.getStatusBarNotification()));
  27. row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
  28. //该句实现了焦点的策略,row(也就是我们的通知)有子控件需要焦点则把焦点交给子控件,否则给row。
  29. if (ENABLE_REMOTE_INPUT) {
  30. row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
  31. }
  32. }

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

  1. private void updateNotification(Entry entry, PackageManager pmUser,
  2. StatusBarNotification sbn, ExpandableNotificationRow row) {
  3. row.setNeedsRedaction(needsRedaction(entry));
  4. boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
  5. boolean isUpdate = mNotificationData.get(entry.key) != null;
  6. boolean wasLowPriority = row.isLowPriority();
  7. row.setIsLowPriority(isLowPriority);
  8. row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
  9. // bind the click event to the content area
  10. mNotificationClicker.register(row, sbn);//给通知栏注册点击事件
  11. // Extract target SDK version.
  12. try {
  13. ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
  14. entry.targetSdk = info.targetSdkVersion;
  15. } catch (NameNotFoundException ex) {
  16. Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
  17. }
  18. row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
  19. && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
  20. entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
  21. entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
  22. entry.row = row;
  23. entry.row.setOnActivatedListener(this);
  24. boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
  25. mNotificationData.getImportance(sbn.getKey()));
  26. boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
  27. row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
  28. row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
  29. row.updateNotification(entry);
  30. }

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

  1. public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
  2. Notification notification = sbn.getNotification();
  3. if (notification.contentIntent != null || notification.fullScreenIntent != null) {
  4. row.setOnClickListener(this);
  5. } else {
  6. row.setOnClickListener(null);
  7. }
  8. }

进入ExpandableNotificationRow的updateNotification方法

  1. public void updateNotification(NotificationData.Entry entry) {
  2. mEntry = entry;
  3. mStatusBarNotification = entry.notification;
  4. mNotificationInflater.inflateNotificationViews();
  5. }

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

移除通知 

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

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

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

  1. public void removeNotification(String key, RankingMap ranking) {
  2. boolean deferRemoval = false;
  3. abortExistingInflation(key);
  4. if (mHeadsUpManager.isHeadsUp(key)) {
  5. // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
  6. // sending look longer than it takes.
  7. // Also we should not defer the removal if reordering isn't allowed since otherwise
  8. // some notifications can't disappear before the panel is closed.
  9. boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key)
  10. && !FORCE_REMOTE_INPUT_HISTORY
  11. || !mVisualStabilityManager.isReorderingAllowed();
  12. deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
  13. }
  14. if (key.equals(mMediaNotificationKey)) {
  15. clearCurrentMediaNotification();
  16. updateMediaMetaData(true, true);
  17. }
  18. if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
  19. Entry entry = mNotificationData.get(key);
  20. StatusBarNotification sbn = entry.notification;
  21. Notification.Builder b = Notification.Builder
  22. .recoverBuilder(mContext, sbn.getNotification().clone());
  23. CharSequence[] oldHistory = sbn.getNotification().extras
  24. .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
  25. CharSequence[] newHistory;
  26. if (oldHistory == null) {
  27. newHistory = new CharSequence[1];
  28. } else {
  29. newHistory = new CharSequence[oldHistory.length + 1];
  30. for (int i = 0; i < oldHistory.length; i++) {
  31. newHistory[i + 1] = oldHistory[i];
  32. }
  33. }
  34. newHistory[0] = String.valueOf(entry.remoteInputText);
  35. b.setRemoteInputHistory(newHistory);
  36. Notification newNotification = b.build();
  37. // Undo any compatibility view inflation
  38. newNotification.contentView = sbn.getNotification().contentView;
  39. newNotification.bigContentView = sbn.getNotification().bigContentView;
  40. newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
  41. StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
  42. sbn.getOpPkg(),
  43. sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
  44. newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
  45. boolean updated = false;
  46. try {
  47. updateNotification(newSbn, null);
  48. updated = true;
  49. } catch (InflationException e) {
  50. deferRemoval = false;
  51. }
  52. if (updated) {
  53. mKeysKeptForRemoteInput.add(entry.key);
  54. return;
  55. }
  56. }
  57. if (deferRemoval) {
  58. mLatestRankingMap = ranking;
  59. mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
  60. return;
  61. }
  62. Entry entry = mNotificationData.get(key);
  63. if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
  64. && (entry.row != null && !entry.row.isDismissed())) {
  65. mLatestRankingMap = ranking;
  66. mRemoteInputEntriesToRemoveOnCollapse.add(entry);
  67. return;
  68. }
  69. if (entry != null && mNotificationGutsExposed != null
  70. && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
  71. && !entry.row.getGuts().isLeavebehind()) {
  72. Log.w(TAG, "Keeping notification because it's showing guts. " + key);
  73. mLatestRankingMap = ranking;
  74. mKeyToRemoveOnGutsClosed = key;
  75. return;
  76. }
  77. if (entry != null) {
  78. mForegroundServiceController.removeNotification(entry.notification);
  79. }
  80. if (entry != null && entry.row != null) {
  81. entry.row.setRemoved();
  82. mStackScroller.cleanUpViewState(entry.row);
  83. }
  84. // Let's remove the children if this was a summary
  85. handleGroupSummaryRemoved(key, ranking);
  86. StatusBarNotification old = removeNotificationViews(key, ranking);
  87. /// M: Enable this log for unusual case debug.
  88. if (true/**SPEW*/) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
  89. if (old != null) {
  90. if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
  91. && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
  92. if (mState == StatusBarState.SHADE) {
  93. animateCollapsePanels();
  94. } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
  95. goToKeyguard();
  96. }
  97. }
  98. }
  99. setAreThereNotifications();
  100. }

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

  1. protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
  2. NotificationData.Entry entry = mNotificationData.remove(key, ranking);
  3. if (entry == null) {
  4. Log.w(TAG, "removeNotification for unknown key: " + key);
  5. return null;
  6. }
  7. updateNotifications();
  8. Dependency.get(LeakDetector.class).trackGarbage(entry);
  9. return entry.notification;
  10. }

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

2.3Navigation bars详解

导航栏的创建

StatusBar.makeStatusBarView

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

NavigationBarFragment.create

  1. public static View create(Context context, FragmentListener listener) {
  2. WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
  3. LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
  4. WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
  5. WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  6. | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  7. | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
  8. | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
  9. | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
  10. | WindowManager.LayoutParams.FLAG_SLIPPERY,
  11. PixelFormat.TRANSLUCENT);
  12. lp.token = new Binder();
  13. lp.setTitle("NavigationBar");
  14. lp.accessibilityTitle = context.getString(R.string.nav_bar);
  15. lp.windowAnimations = 0;
  16. View navigationBarView = LayoutInflater.from(context).inflate(
  17. R.layout.navigation_bar_window, null);
  18. if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
  19. if (navigationBarView == null) return null;
  20. context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
  21. FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
  22. NavigationBarFragment fragment = new NavigationBarFragment();
  23. fragmentHost.getFragmentManager().beginTransaction()
  24. .replace(R.id.navigation_bar_frame, fragment, TAG)
  25. .commit();
  26. fragmentHost.addTagListener(TAG, listener);
  27. return navigationBarView;
  28. }

上述代码做了两件事:

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

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

navigation_bar.xml

  1. <com.android.systemui.statusbar.phone.NavigationBarView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:systemui="http://schemas.android.com/apk/res-auto"
  4. android:layout_height="match_parent"
  5. android:layout_width="match_parent"
  6. android:background="@drawable/system_bar_background">
  7. <com.android.systemui.statusbar.phone.NavigationBarInflaterView
  8. android:id="@+id/navigation_inflater"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent" />
  11. </com.android.systemui.statusbar.phone.NavigationBarView>

NavigationBarView 完成资源文件添加

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

NavigationBarFragment

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

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/120596
推荐阅读
相关标签
  

闽ICP备14008679号