SystemUI之状态栏的通知显示

一、通知栏的布局的添加

status_bar.xml中的左边区域增加了通知图标显示的区域

<LinearLayout
    android:id="@+id/status_bar_left_side"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:clipChildren="false"
>
.......................
//notification icon显示区域
    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
        android:id="@+id/notification_icon_area"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:clipChildren="false"/>

</LinearLayout>

StatusBar添加fragment时初始化NotificationIconArea

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    ..................
    FragmentHostManager.get(mStatusBarWindow)
            .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                CollapsedStatusBarFragment statusBarFragment =
                        (CollapsedStatusBarFragment) fragment;
                statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);//初始化通知栏区域
                PhoneStatusBarView oldStatusBarView = mStatusBarView;
                ..................

CollapsedStatusBarFragment.initNotifcationIconArea

public void initNotificationIconArea(NotificationIconAreaController
        notificationIconAreaController) {
    ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
    mNotificationIconAreaInner =
            notificationIconAreaController.getNotificationInnerAreaView();//
    if (mNotificationIconAreaInner.getParent() != null) {
        ((ViewGroup) mNotificationIconAreaInner.getParent())
                .removeView(mNotificationIconAreaInner);
    }
    notificationIconArea.addView(mNotificationIconAreaInner);
    ...............
    showNotificationIconArea(false);
}

mNotificationIconAreaInner为notification_icon_area.xml,先判断是否存在view,通过view的父布局将其自身remove,然后再addview。即把notification_icon_area.xml的view添加到notification_icon_area的view里作为子视图

当下拉状态栏时,状态栏的图标ico会慢慢变为透明。

//隐藏系统icon区域
public void hideSystemIconArea(boolean animate) {
    animateHide(mSystemIconArea, animate);
}
//隐藏通知icon区域
public void hideNotificationIconArea(boolean animate) {
    animateHide(mNotificationIconAreaInner, animate);
    animateHide(mCenteredIconArea, animate);
}

二、通知的监听加载流程

StatusBar.start添加Notification的注册监听,最终是在NotificationListenerWithPlugins注册的

com.android.systemui.statusbar.phone.NotificationListenerWithPlugins.java
public void registerAsSystemService(Context context, ComponentName componentName,
        int currentUser) throws RemoteException {
    super.registerAsSystemService(context, componentName, currentUser);
    Dependency.get(PluginManager.class).addPluginListener(this,
            NotificationListenerController.class);
}

在NotificationListener处理通知监听的回调

com.android.systemui.statusbar.NotificationListener.java
//监听连接
public void onListenerConnected() {
    if (DEBUG) Log.d(TAG, "onListenerConnected");
    onPluginConnected();
    final StatusBarNotification[] notifications = getActiveNotifications();//所有激活的通知,即没有被移除的通知
    if (notifications == null) {
        Log.w(TAG, "onListenerConnected unable to get active notifications.");
        return;
    }
    final RankingMap currentRanking = getCurrentRanking();
    Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
        for (StatusBarNotification sbn : notifications) {
            mEntryManager.addNotification(sbn, currentRanking);//添加到通知实体类进行管理
        }
    });
    NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
    onSilentStatusBarIconsVisibilityChanged(noMan.shouldHideSilentStatusBarIcons());
}

@Override
public void onNotificationPosted(final StatusBarNotification sbn,
        final RankingMap rankingMap) {
    .....................
                //收到更新或添加的通知
                if (isUpdate) {
                    mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
                } else {
                    mEntryManager.getNotificationData()
                            .updateRanking(rankingMap);
                }
                return;
            }
            if (isUpdate) {
                mEntryManager.updateNotification(sbn, rankingMap);
            } else {
                mEntryManager.addNotification(sbn, rankingMap);
            }
        });
    }
}

//删除通知
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
        int reason) {
    if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn + " reason: " + reason);
    if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
        final String key = sbn.getKey();
        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
            mEntryManager.removeNotification(key, rankingMap, reason);
        });
    }
}
//按通知优先级,重新排列通知位置
public void onNotificationRankingUpdate(final RankingMap rankingMap) {
    if (DEBUG) Log.d(TAG, "onRankingUpdate");
    if (rankingMap != null) {
        RankingMap r = onPluginRankingUpdate(rankingMap);
        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
            mEntryManager.updateNotificationRanking(r);
        });
    }
}

我们重点看看是如何添加新通知的

public void addNotification(StatusBarNotification notification,
        NotificationListenerService.RankingMap ranking) {
    try {
        addNotificationInternal(notification, ranking);
    } catch (InflationException e) {
        handleInflationException(notification, e);
    }
}

private void addNotificationInternal(StatusBarNotification notification,
        NotificationListenerService.RankingMap rankingMap) throws InflationException {
    String key = notification.getKey();
    mNotificationData.updateRanking(rankingMap);
    NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
    rankingMap.getRanking(key, ranking);

    NotificationEntry entry = new NotificationEntry(notification, ranking);

    Dependency.get(LeakDetector.class).trackInstance(entry);
    // Construct the expanded view.
    requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
            REASON_CANCEL));
  .............
}

先对通知按优先级进行排列,然后创建出NotificationEntry的实体,最后通过inflate添加到布局中。requireBinder()中返回的是NotificationRowBinder,而NotficationRowBinder是由setRowBinder传入的,setRowBinder由StatusBar.setUpPresenter调用,参数是rowBinder。

private void setUpPresenter() {
   ....................
    final NotificationRowBinderImpl rowBinder =
            new NotificationRowBinderImpl(
                    mContext,
                    SystemUIFactory.getInstance().provideAllowNotificationLongPress());
  ........................
    mEntryManager.setRowBinder(rowBinder);
  .....................
}

public void setRowBinder(NotificationRowBinder notificationRowBinder) {
    mNotificationRowBinder = notificationRowBinder;
}

private NotificationRowBinder requireBinder() {
    ..............
    return mNotificationRowBinder;
}

最终是进入到了NotificationRowBinderImpl.inflateViews函数里

com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl.java
public void inflateViews(
        NotificationEntry entry,
        Runnable onDismissRunnable)
        throws InflationException {
    ViewGroup parent = mListContainer.getViewParentForNotification(entry);
    PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
            entry.notification.getUser().getIdentifier());

    final StatusBarNotification sbn = entry.notification;
    //存在就更新通知,不存在就装载布局
    if (entry.rowExists()) {
        entry.updateIcons(mContext, sbn);
        entry.reset();
        updateNotification(entry, pmUser, sbn, entry.getRow());
    } else {
        entry.createIcons(mContext, sbn);
        new RowInflaterTask().inflate(mContext, parent, entry,
                row -> {
                    bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                    updateNotification(entry, pmUser, sbn, row);
                });
    }
}

我们先看是如何装载布局的

public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
        RowInflationFinishedListener listener) {
    if (TRACE_ORIGIN) {
        mInflateOrigin = new Throwable("inflate requested here");
    }
    mListener = listener;
    AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
    mEntry = entry;
    entry.setInflationTask(this);
    inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

最后是把status_bar_notification_row.xml的布局添加上了

我们接着看是如何更新通知的

private void updateNotification(
        NotificationEntry entry,
        PackageManager pmUser,
        StatusBarNotification sbn,
        ExpandableNotificationRow row) {
    row.setIsLowPriority(entry.ambient);

    // Extract target SDK version.
    try {
        ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
        entry.targetSdk = info.targetSdkVersion;
    } catch (PackageManager.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.setRow(row);
    row.setOnActivatedListener(mPresenter);

    boolean useIncreasedCollapsedHeight =
            mMessagingUtil.isImportantMessaging(sbn, entry.importance);
    boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
            && !mPresenter.isPresenterFullyCollapsed();
    row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
    row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
    row.setEntry(entry);

    if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
        row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
    }
    if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
        row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
    }
    row.setNeedsRedaction(
            Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
    row.inflateViews();//装入view
   ..............
}

rom.inflateViews最后是调用到了inflateNotificationViews

com.android.systemui.statusbar.notification.row.NotificationContentInflater.java
private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
   ............
    AsyncInflationTask task = new AsyncInflationTask(
            sbn,
            mInflateSynchronously,
            reInflateFlags,
            mCachedContentViews,
            mRow,
            mIsLowPriority,
            mIsChildInGroup,
            mUsesIncreasedHeight,
            mUsesIncreasedHeadsUpHeight,
            mRedactAmbient,
            mCallback,
            mRemoteViewClickHandler);
    if (mInflateSynchronously) {
        task.onPostExecute(task.doInBackground());
    } else {
        task.executeOnExecutor(Executors.newCachedThreadPool()); // UNISOC: Modify for bug1340757
    }
}

AsyncInflationTask继承了asynctask,所以我们看它的继承方法

protected InflationProgress doInBackground(Void... params) {
    try {
        final Notification.Builder recoveredBuilder
                = Notification.Builder.recoverBuilder(mContext,
                mSbn.getNotification());

        Context packageContext = mSbn.getPackageContext(mContext);
        Notification notification = mSbn.getNotification();
        if (notification.isMediaNotification()) {
            MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                    packageContext);
            processor.processNotification(notification, recoveredBuilder);
        }
        //创建远程view
        InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
                recoveredBuilder, mIsLowPriority,
                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
                mRedactAmbient, packageContext);
        return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
                mRow.getContext(), mRow.getHeadsUpManager(),
                mRow.getExistingSmartRepliesAndActions());
    } catch (Exception e) {
        mError = e;
        return null;
    }
}

@Override
protected void onPostExecute(InflationProgress result) {
    if (mError == null) {
        mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
                mCachedContentViews, mRow, mRedactAmbient, mRemoteViewClickHandler, this);
    } else {
        handleError(mError);
    }
}

首先判断是否是媒体通知,进行对应的主题背景颜色绘制。继续看方法createRemoteViews是如何创建远程view的

private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
        Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
        boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
        Context packageContext) {
    InflationProgress result = new InflationProgress();
    isLowPriority = isLowPriority && !isChildInGroup;

    if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
        result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
    }

    if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
        result.newExpandedView = createExpandedView(builder, isLowPriority);
    }

    if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
        result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
    }

    if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
        result.newPublicView = builder.makePublicContentView();
    }

    if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
        result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                : builder.makeAmbientNotification();
    }
    result.packageContext = packageContext;
    result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
    result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
            true /* showingPublic */);
    return result;
}

createRemoteViews就是根据各种flag创建各式各样的view,如CONTENT_VIEW、EXPANDED_VIEW、HEADSUP_VIEW、PUBLIC_VIEW、AMBIENT_VIEW等

三、总结:总的来说通知的布局添加不难,逻辑都比较清晰,但是涉及到布局装载、布局的嵌套还是有点繁琐,此外还涉及到布局的许多细节。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值