1.简介
先看ui,再看数据来源
- dot是4.4画出来的,颜色是2.3读取的
- 小节7,8,9主要学习下launcher里通知服务的启动,数据的刷新问题。
2.BubbleTextView.java
桌面用到的图标基本都是这个控件或者它的子类
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
IconLabelDotView, DraggableView, Reorderable {
2.1.onDraw
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawDotIfNecessary(canvas);
}
//在右上角画通知未读提示
protected void drawDotIfNecessary(Canvas canvas) {
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
//见补充2,根据iconSize和view的宽,获取dot需要的bounds
getIconBounds(mDotParams.iconBounds);
//3.1,bounds进行缩小处理
Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
IconShape.getNormalizationScale());//0.92f
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
//见4.4
mDotRenderer.draw(canvas, mDotParams);
canvas.translate(-scrollX, -scrollY);
}
}
>1.hasDot
private boolean hasDot() {
return mDotInfo != null;
}
>2.getIconBounds
public void getIconBounds(Rect outBounds) {
getIconBounds(mIconSize, outBounds);
}
public void getIconBounds(int iconSize, Rect outBounds) {
outBounds.set(0, 0, iconSize, iconSize);
if (mLayoutHorizontal) {
int top = (getHeight() - iconSize) / 2;
if (mIsRtl) {
outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), top);
} else {
outBounds.offsetTo(getPaddingLeft(), top);
}
} else {
//水平方向居中,顶部偏移paddingTop
outBounds.offset((getWidth() - iconSize) / 2, getPaddingTop());
}
}
2.2.applyDotState
public void applyDotState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasDotted = mDotInfo != null;
//见补充2
mDotInfo = mActivity.getDotInfoForItem(itemInfo);
boolean isDotted = mDotInfo != null;
float newDotScale = isDotted ? 1f : 0;
//两种render,见补充1
if (mDisplay == DISPLAY_ALL_APPS) {
mDotRenderer = mActivity.getDeviceProfile().mDotRendererAllApps;
} else {
mDotRenderer = mActivity.getDeviceProfile().mDotRendererWorkSpace;
}
if (wasDotted || isDotted) {
// Animate when a dot is first added or when it is removed.
if (animate && (wasDotted ^ isDotted) && isShown()) {
animateDotScale(newDotScale);
} else {
cancelDotScaleAnim();
mDotParams.scale = newDotScale;
invalidate();
}
}
if (!TextUtils.isEmpty(itemInfo.contentDescription)) {
if (itemInfo.isDisabled()) {
setContentDescription(getContext().getString(R.string.disabled_app_label,
itemInfo.contentDescription));
} else if (hasDot()) {
int count = mDotInfo.getNotificationCount();
setContentDescription(
getAppLabelPluralString(itemInfo.contentDescription.toString(), count));
} else {
setContentDescription(itemInfo.contentDescription);
}
}
}
}
>1.DeviceProfile.java
private static final int DEFAULT_DOT_SIZE = 100;
//可以看到,就是大小不一样
mDotRendererWorkSpace = createDotRenderer(iconSizePx, dotRendererCache);
mDotRendererAllApps = createDotRenderer(allAppsIconSizePx, dotRendererCache);
private static DotRenderer createDotRenderer(
int size, @NonNull SparseArray<DotRenderer> cache) {
DotRenderer renderer = cache.get(size);
if (renderer == null) {
//见小节4
renderer = new DotRenderer(size, getShapePath(DEFAULT_DOT_SIZE), DEFAULT_DOT_SIZE);
cache.put(size, renderer);
}
return renderer;
}
>2.getDotInfoForItem
PopupDataProvider.java
public @Nullable DotInfo getDotInfoForItem(@NonNull ItemInfo info) {
if (!ShortcutUtil.supportsShortcuts(info)) {
return null;
}
//先从map里读取dot数据
DotInfo dotInfo = mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
if (dotInfo == null) {
return null;
}
//dotInfo不为空的话再转化数据
List<NotificationKeyData> notifications = getNotificationsForItem(
info, dotInfo.getNotificationKeys());
if (notifications.isEmpty()) {
return null;
}
return dotInfo;
}
2.3.applyIconAndLabel
protected void applyIconAndLabel(ItemInfoWithIcon info) {
boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
|| mDisplay == DISPLAY_TASKBAR;
int flags = useTheme ? FLAG_THEMED : 0;
if (mHideBadge) {
flags |= FLAG_NO_BADGE;
}
//见6.1
FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
//dot两种颜色设置
//这个是应用图标的主色调
mDotParams.appColor = iconDrawable.getIconColor();
//这个是dot的颜色,读取的配置文件
mDotParams.dotColor = getContext().getResources()
.getColor(android.R.color.system_accent3_200, getContext().getTheme());
setIcon(iconDrawable);
applyLabel(info);
}
2.4.applyFromWorkspaceItem
workspace页面的数据走这里,包括hotseat
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
//见2.3
applyIconAndLabel(info);
//补充1,就是设置tag
setItemInfo(info);
//进度条
applyLoadingState(promiseStateChanged);
//见2.2
applyDotState(info, false /* animate */);
setDownloadStateContentDescription(info, info.getProgressLevel());
}
>1.setItemInfo
protected void setItemInfo(ItemInfoWithIcon itemInfo) {
setTag(itemInfo);
}
2.5.applyFromApplicationInfo
allApps页面的数据走这里
public void applyFromApplicationInfo(AppInfo info) {
applyIconAndLabel(info);
// We don't need to check the info since it's not a WorkspaceItemInfo
setItemInfo(info);
// Verify high res immediately
verifyHighRes();
if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
applyProgressLevel();
}
applyDotState(info, false /* animate */);
setDownloadStateContentDescription(info, info.getProgressLevel());
}
3.Utilities.java
3.1.scaleRectAboutCenter
从中心点开始缩小
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {
int cx = r.centerX();
int cy = r.centerY();
r.offset(-cx, -cy);
r.left = (int) (r.left * scale + 0.5f);
r.top = (int) (r.top * scale + 0.5f);
r.right = (int) (r.right * scale + 0.5f);
r.bottom = (int) (r.bottom * scale + 0.5f);
r.offset(cx, cy);
}
}
4.DotRenderer.java
4.1.构造方法
private static final float SIZE_PERCENTAGE = 0.228f;
public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
//圆点的大小和icon的大小有关,百分比见上边的常量
int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
if (size <= 0) {
size = MIN_DOT_SIZE;
}
ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
builder.ambientShadowAlpha = 88;
//见4.2,生成背景图
mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
mCircleRadius = builder.radius;
mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
//找到路径上最接近左上角和右上角的点
mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
}
4.2.ShadowGenerator
>1.setupBlurForSize
public Builder setupBlurForSize(int height) {
if (ENABLE_SHADOWS) {//走这里
shadowBlur = height * 1f / 24;
keyShadowDistance = height * 1f / 16;
} else {
shadowBlur = 0;
keyShadowDistance = 0;
}
return this;
}
>2.createPill
创建圆点bitmap
public Bitmap createPill(int width, int height) {
return createPill(width, height, height / 2f);
}
public Bitmap createPill(int width, int height, float r) {
radius = r;
int centerX = Math.round(width / 2f + shadowBlur);
int centerY = Math.round(radius + shadowBlur + keyShadowDistance);
int center = Math.max(centerX, centerY);
bounds.set(0, 0, width, height);
bounds.offsetTo(center - width / 2f, center - height / 2f);
int size = center * 2;
//见4.3.1,核心就是回调方法,也就是补充3
return BitmapRenderer.createHardwareBitmap(size, size, this::drawShadow);
}
>3.drawShadow
圆点背景图就是这里画出来的
public void drawShadow(Canvas c) {
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
p.setColor(color);
if (ENABLE_SHADOWS) {
// Key shadow
p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
setColorAlphaBound(Color.BLACK, keyShadowAlpha));//见5.2
c.drawRoundRect(bounds, radius, radius, p);
// Ambient shadow
p.setShadowLayer(shadowBlur, 0, 0,
setColorAlphaBound(Color.BLACK, ambientShadowAlpha));
c.drawRoundRect(bounds, radius, radius, p);
}
if (Color.alpha(color) < 255) {
// Clear any content inside the pill-rect for translucent fill.
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
p.clearShadowLayer();
p.setColor(Color.BLACK);
c.drawRoundRect(bounds, radius, radius, p);
p.setXfermode(null);
p.setColor(color);
c.drawRoundRect(bounds, radius, radius, p);
}
}
4.3.BitmapRenderer.java
>1.createHardwareBitmap
static Bitmap createHardwareBitmap(int width, int height, BitmapRenderer renderer) {
if (!USE_HARDWARE_BITMAP) {
return createSoftwareBitmap(width, height, renderer);
}
//走这里
GraphicsUtils.noteNewBitmapCreated();
Picture picture = new Picture();
renderer.draw(picture.beginRecording(width, height));
picture.endRecording();
return Bitmap.createBitmap(picture);
}
4.4.draw
public void draw(Canvas canvas, DrawParams params) {
if (params == null) {
return;
}
canvas.save();
//获取dot中心点位置,数据见2.1
Rect iconBounds = params.iconBounds;
float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition;
float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0];
float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1];
// Ensure dot fits entirely in canvas clip bounds.
Rect canvasBounds = canvas.getClipBounds();
float offsetX = params.leftAlign
? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset))
: Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset));
float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset));
// We draw the dot relative to its center.
canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY);
canvas.scale(params.scale, params.scale);
//画背景
mCirclePaint.setColor(Color.BLACK);
//mBackgroundWithShadow是4.1构造方法里初始化的
canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
//画上层圆点
mCirclePaint.setColor(params.dotColor);//颜色是2.3里设置的
canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
canvas.restore();
}
5.GraphicsUtils.java
5.1.getShapePath
public static Path getShapePath(int size) {
//前景色和背景色
AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
//设置大小
drawable.setBounds(0, 0, size, size);
//获取路径
return new Path(drawable.getIconMask());
}
5.2.setColorAlphaBound
移除color里原本的透明度,用参数里的alpha
public static int setColorAlphaBound(int color, int alpha) {
if (alpha < 0) {
alpha = 0;
} else if (alpha > 255) {
alpha = 255;
}
return (color & 0x00ffffff) | (alpha << 24);
}
6.ItemInfoWithIcon.java
public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
6.1.newIcon
public FastBitmapDrawable newIcon(Context context) {
return newIcon(context, 0);
}
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
drawable.setIsDisabled(isDisabled());
return drawable;
}
6.2.BitmapInfo.java
>1.LOW_RES_INFO
public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON);//参考补充2
>2.fromBitmap
public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
return of(bitmap, 0);
}
public static BitmapInfo of(@NonNull Bitmap bitmap, int color) {
return new BitmapInfo(bitmap, color);
}
>3.newIcon
public FastBitmapDrawable newIcon(Context context) {
return newIcon(context, 0);
}
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
FastBitmapDrawable drawable;
if (isLowRes()) {
drawable = new PlaceHolderIconDrawable(this, context);
} else if ((creationFlags & FLAG_THEMED) != 0 && mMono != null) {
drawable = ThemedIconDrawable.newDrawable(this, context);
} else {
drawable = new FastBitmapDrawable(this);
}
applyFlags(context, drawable, creationFlags);
return drawable;
}
7.PopupDataProvider.java
这个就是通知改变的监听回调,见8.5.1里添加的回调。
public class PopupDataProvider implements NotificationListener.NotificationsChangedListener {
7.1.onNotificationPosted
新发送的通知
public void onNotificationPosted(PackageUserKey postedPackageUserKey,
NotificationKeyData notificationKey) {
DotInfo dotInfo = mPackageUserToDotInfos.get(postedPackageUserKey);
if (dotInfo == null) {
dotInfo = new DotInfo();
mPackageUserToDotInfos.put(postedPackageUserKey, dotInfo);
}
if (dotInfo.addOrUpdateNotificationKey(notificationKey)) {
updateNotificationDots(postedPackageUserKey::equals);
}
}
7.2.onNotificationRemoved
通知被删除了
public void onNotificationRemoved(PackageUserKey removedPackageUserKey,
NotificationKeyData notificationKey) {
DotInfo oldDotInfo = mPackageUserToDotInfos.get(removedPackageUserKey);
if (oldDotInfo != null && oldDotInfo.removeNotificationKey(notificationKey)) {
if (oldDotInfo.getNotificationKeys().size() == 0) {
mPackageUserToDotInfos.remove(removedPackageUserKey);
}
updateNotificationDots(removedPackageUserKey::equals);
trimNotifications(mPackageUserToDotInfos);
}
}
7.3.onNotificationFullRefresh
通知数据完整刷新
public void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications) {
if (activeNotifications == null) return;
// This will contain the PackageUserKeys which have updated dots.
HashMap<PackageUserKey, DotInfo> updatedDots = new HashMap<>(mPackageUserToDotInfos);
mPackageUserToDotInfos.clear();
for (StatusBarNotification notification : activeNotifications) {
PackageUserKey packageUserKey = PackageUserKey.fromNotification(notification);
DotInfo dotInfo = mPackageUserToDotInfos.get(packageUserKey);
if (dotInfo == null) {
dotInfo = new DotInfo();
mPackageUserToDotInfos.put(packageUserKey, dotInfo);
}
dotInfo.addOrUpdateNotificationKey(NotificationKeyData.fromNotification(notification));
}
// Add and remove from updatedDots so it contains the PackageUserKeys of updated dots.
for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
DotInfo prevDot = updatedDots.get(packageUserKey);
DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
if (prevDot == null
|| prevDot.getNotificationCount() != newDot.getNotificationCount()) {
updatedDots.put(packageUserKey, newDot);
} else {
// No need to update the dot if it already existed (no visual change).
// Note that if the dot was removed entirely, we wouldn't reach this point because
// this loop only includes active notifications added above.
updatedDots.remove(packageUserKey);
}
}
if (!updatedDots.isEmpty()) {
updateNotificationDots(updatedDots::containsKey);
}
trimNotifications(updatedDots);
}
8.NotificationListener.java
public class NotificationListener extends NotificationListenerService {
这个服务的启动逻辑见上篇分析
8.1.服务注册
<service
android:name="com.android.launcher3.notification.NotificationListener"
android:label="@string/notification_dots_service_title"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
8.2.onNotificationXXX
实现通知的改变移除等处理
public void onNotificationPosted(final StatusBarNotification sbn) {
if (sbn != null) {
//见补充1
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, sbn).sendToTarget();
}
}
public void onNotificationRemoved(final StatusBarNotification sbn) {
if (sbn != null) {
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, sbn).sendToTarget();
}
}
>1.handleWorkerMessage
private boolean handleWorkerMessage(Message message) {
switch (message.what) {
case MSG_NOTIFICATION_POSTED: {
StatusBarNotification sbn = (StatusBarNotification) message.obj;
//处理下数据再交给8.3处理
mUiHandler.obtainMessage(notificationIsValidForUI(sbn)
? MSG_NOTIFICATION_POSTED : MSG_NOTIFICATION_REMOVED,
toKeyPair(sbn)).sendToTarget();
return true;
}
//其他case省略。。
case MSG_NOTIFICATION_FULL_REFRESH://添加listener的时候或者断开连接的时候调用
List<StatusBarNotification> activeNotifications = null;
if (sIsConnected) {
//获取当前说有的通知
activeNotifications = Arrays.stream(getActiveNotificationsSafely(null))
.filter(this::notificationIsValidForUI)
.collect(Collectors.toList());
} else {
activeNotifications = new ArrayList<>();
}
mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
return true;
8.3.handleUiMessage
可以看到,就是交给listener处理了,回调见8.5添加
private boolean handleUiMessage(Message message) {
switch (message.what) {
case MSG_NOTIFICATION_POSTED:
if (sNotificationsChangedListeners.size() > 0) {
Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
listener.onNotificationPosted(msg.first, msg.second);
}
}
break;
case MSG_NOTIFICATION_REMOVED:
if (sNotificationsChangedListeners.size() > 0) {
Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
listener.onNotificationRemoved(msg.first, msg.second);
}
}
break;
case MSG_NOTIFICATION_FULL_REFRESH:
if (sNotificationsChangedListeners.size() > 0) {
for (NotificationsChangedListener listener : sNotificationsChangedListeners) {
listener.onNotificationFullRefresh(
(List<StatusBarNotification>) message.obj);
}
}
break;
}
return true;
}
8.4.onListenerConnected
监听已连接
public void onListenerConnected() {
super.onListenerConnected();
sIsConnected = true;
// Register an observer to rebind the notification listener when dots are re-enabled.
mSettingsCache = SettingsCache.INSTANCE.get(this);
mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
//监听设置里是否允许通知未读徽章
mSettingsCache.register(NOTIFICATION_BADGING_URI,
mNotificationSettingsChangedListener);
//见补充1
onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
onNotificationFullRefresh();
}
>1.onNotificationSettingsChanged
如果不允许徽章并且已经连接的情况,那么断开连接。
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
if (!areNotificationDotsEnabled && sIsConnected) {
requestUnbind();
}
}
>2.NOTIFICATION_BADGING_URI
//通知未读提示是否允许
public static final Uri NOTIFICATION_BADGING_URI =
Settings.Secure.getUriFor("notification_badging");
>3.LauncherAppState
监听改变,重新请求bind的
SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged;
settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister);
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
if (areNotificationDotsEnabled) {
//重新绑定
NotificationListener.requestRebind(new ComponentName(
mContext, NotificationListener.class));
}
}
8.5.addNotificationsChangedListener
调用这个方法添加listener的地方有两处,见补充1和2
public static void addNotificationsChangedListener(NotificationsChangedListener listener) {
if (listener == null) {
return;
}
sNotificationsChangedListeners.add(listener);
NotificationListener notificationListener = getInstanceIfConnected();
if (notificationListener != null) {
notificationListener.onNotificationFullRefresh();
} else {
MODEL_EXECUTOR.submit(() -> MAIN_EXECUTOR.submit(() ->
listener.onNotificationFullRefresh(Collections.emptyList())));
}
}
>1.Launcher.java
//见小节9
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
// Set the notification listener and fetch updated notifications when we resume
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mWorkspace.updateNotificationDots(updatedDots);
mAppsView.getAppsStore().updateNotificationDots(updatedDots);
}
>2.TaskbarPopupController.java
public TaskbarPopupController(TaskbarActivityContext context) {
mContext = context;
//实例化对象
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
//添加监听
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
}
看起来是更新的展开的folder里的图标未读消息状态的
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
|| updatedDots.test(packageUserKey);
LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
if (matcher.test(info)) {
((BubbleTextView) v).applyDotState(info, true /* animate */);
}
} else if (info instanceof FolderInfo && v instanceof FolderIcon) {
FolderInfo fi = (FolderInfo) info;
if (fi.contents.stream().anyMatch(matcher)) {
FolderDotInfo folderDotInfo = new FolderDotInfo();
for (WorkspaceItemInfo si : fi.contents) {
folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
}
((FolderIcon) v).setDotInfo(folderDotInfo);
}
}
// process all the shortcuts
return false;
};
mControllers.taskbarViewController.mapOverItems(op);
Folder folder = Folder.getOpen(mContext);
if (folder != null) {
folder.iterateOverItems(op);
}
}
9.PopupDataProvider
PopupDataProvider实现了通知监听的接口
public class PopupDataProvider implements NotificationListener.NotificationsChangedListener {
9.1.onNotificationFullRefresh
数据来源见8.2.1以及8.3,完整刷新所有通知数据
public void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications) {
if (activeNotifications == null) return;
//保存旧数据
HashMap<PackageUserKey, DotInfo> updatedDots = new HashMap<>(mPackageUserToDotInfos);
//清空
mPackageUserToDotInfos.clear();
for (StatusBarNotification notification : activeNotifications) {
PackageUserKey packageUserKey = PackageUserKey.fromNotification(notification);
DotInfo dotInfo = mPackageUserToDotInfos.get(packageUserKey);
if (dotInfo == null) {
dotInfo = new DotInfo();
//新加
mPackageUserToDotInfos.put(packageUserKey, dotInfo);
}
//数据转化并加入集合或者更新集合里的数据 dotInfo.addOrUpdateNotificationKey(NotificationKeyData.fromNotification(notification));
}
// Add and remove from updatedDots so it contains the PackageUserKeys of updated dots.
for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
//新旧数据
DotInfo prevDot = updatedDots.get(packageUserKey);
DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
//旧dot数据为空或者新旧通知数不一样
if (prevDot == null
|| prevDot.getNotificationCount() != newDot.getNotificationCount()) {
//更新
updatedDots.put(packageUserKey, newDot);
} else {
//dot数据没有变化,则移除
updatedDots.remove(packageUserKey);
}
}
if (!updatedDots.isEmpty()) {
//需要更新的
updateNotificationDots(updatedDots::containsKey);
}
trimNotifications(updatedDots);
}
9.2.onNotificationPosted
public void onNotificationPosted(PackageUserKey postedPackageUserKey,
NotificationKeyData notificationKey) {
DotInfo dotInfo = mPackageUserToDotInfos.get(postedPackageUserKey);
if (dotInfo == null) {
dotInfo = new DotInfo();
mPackageUserToDotInfos.put(postedPackageUserKey, dotInfo);
}
//添加或者更新成功
if (dotInfo.addOrUpdateNotificationKey(notificationKey)) {
//更新
updateNotificationDots(postedPackageUserKey::equals);
}
}
9.3.onNotificationRemoved
public void onNotificationRemoved(PackageUserKey removedPackageUserKey,
NotificationKeyData notificationKey) {
DotInfo oldDotInfo = mPackageUserToDotInfos.get(removedPackageUserKey);
if (oldDotInfo != null && oldDotInfo.removeNotificationKey(notificationKey)) {
if (oldDotInfo.getNotificationKeys().size() == 0) {
mPackageUserToDotInfos.remove(removedPackageUserKey);
}
updateNotificationDots(removedPackageUserKey::equals);
trimNotifications(mPackageUserToDotInfos);
}
}