- 应用层调用过程,先生成一个notification对象,然后通过调用NotificationManager的norify方法,把nofication发布出去。
MainActivity.java
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel("1000", "test notification", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
Notification.Builder builder = new Notification.Builder(this, notificationChannel.getId());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("hello");
builder.setContentText("for test");
Notification notification = builder.build();
notificationManager.notify(1000, notification);
- notify 【通知,公布】 是NotificationManager中的方法
frameworks/base/core/java/android/app/NotificationManager.java
public void notify(int id, Notification notification)
{
notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification)
{
notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
INotificationManager service = getService();
String pkg = mContext.getPackageName();
// Fix the notification as best we can.
Notification.addFieldsFromContext(mContext, notification);
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
notification.sound.checkFileUriExposed("Notification.sound");
}
}
fixLegacySmallIcon(notification, pkg);
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (notification.getSmallIcon() == null) {
throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ notification);
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- 下面通过RPC到service端
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Notification notification, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Binder.getCallingPid(), tag, id, notification, userId);
}
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int incomingUserId) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
checkCallerIsSystemOrSameApp(pkg);
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
if (pkg == null || notification == null) {
throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
// The system can post notifications for any package, let us resolve that.
final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
// Fix the notification as best we can.
try {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
return;
}
mUsageStats.registerEnqueuedByApp(pkg);
// setup local book-keeping
String channelId = notification.getChannelId();
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
notificationUid, channelId, false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
+ ", channelId=" + channelId
+ ", id=" + id
+ ", tag=" + tag
+ ", opPkg=" + opPkg
+ ", callingUid=" + callingUid
+ ", userId=" + userId
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Log.e(TAG, noChannelStr);
doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
"See log for more details");
return;
}
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
return;
}
// Whitelist pending intents.
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
WHITELIST_TOKEN, duration);
}
}
}
}
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
protected class EnqueueNotificationRunnable implements Runnable {
private final NotificationRecord r;
private final int userId;
EnqueueNotificationRunnable(int userId, NotificationRecord r) {
this.userId = userId;
this.r = r;
};
@Override
public void run() {
synchronized (mNotificationLock) {
mEnqueuedNotifications.add(r);
scheduleTimeoutLocked(r);
final StatusBarNotification n = r.sbn;
if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
NotificationRecord old = mNotificationsByKey.get(n.getKey());
if (old != null) {
// Retain ranking information from previous record
r.copyRankingInformation(old);
}
final int callingUid = n.getUid();
final int callingPid = n.getInitialPid();
final Notification notification = n.getNotification();
final String pkg = n.getPackageName();
final int id = n.getId();
final String tag = n.getTag();
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
handleGroupedNotificationLocked(r, old, callingUid, callingPid);
// if this is a group child, unsnooze parent summary
if (n.isGroup() && notification.isGroupChild()) {
mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
}
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {
int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
if (old != null) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
}
EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
pkg, id, tag, userId, notification.toString(),
enqueueStatus);
}
mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mNotificationAssistants.isEnabled()) {
mNotificationAssistants.onNotificationEnqueued(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
mHandler.post(new PostNotificationRunnable(r.getKey()));
}
}
}
}
protected class PostNotificationRunnable implements Runnable {
private final String key;
PostNotificationRunnable(String key) {
this.key = key;
}
@Override
public void run() {
synchronized (mNotificationLock) {
try {
NotificationRecord r = null;
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
if (Objects.equals(key, enqueued.getKey())) {
r = enqueued;
break;
}
}
if (r == null) {
Slog.i(TAG, "Cannot find enqueued record for key: " + key);
return;
}
NotificationRecord old = mNotificationsByKey.get(key);
final StatusBarNotification n = r.sbn;
final Notification notification = n.getNotification();
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
} else {
old = mNotificationList.get(index);
mNotificationList.set(index, r);
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
}
mNotificationsByKey.put(n.getKey(), r);
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
notification.flags |= Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR;
}
applyZenModeLocked(r);
mRankingHelper.sort(mNotificationList);
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(n, oldSbn);
mHandler.post(new Runnable() {
@Override
public void run() {
mGroupHelper.onNotificationPosted(n);
}
});
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n,
NotificationListenerService.REASON_ERROR);
mHandler.post(new Runnable() {
@Override
public void run() {
mGroupHelper.onNotificationRemoved(n);
}
});
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
// notifications
Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+ n.getPackageName());
}
buzzBeepBlinkLocked(r);
} finally {
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
if (Objects.equals(key, enqueued.getKey())) {
mEnqueuedNotifications.remove(i);
break;
}
}
}
}
}
}
- NotificationManagerService 的内部类 NotificationListeners
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
// Lazily initialized snapshots of the notification.
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : getServices()) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
// This notification hasn't been and still isn't visible -> ignore.
if (!oldSbnVisible && !sbnVisible) {
continue;
}
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
// This notification became invisible -> remove the old one.
if (oldSbnVisible && !sbnVisible) {
final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
mHandler.post(new Runnable() {
@Override
public void run() {
notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
}
});
continue;
}
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyPosted(info, sbnToPost, update);
}
});
}
}
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
listener.onNotificationPosted(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
至此,调用过程完成。
- 更进一步,需要了解getServices()返回的变量的初始化及赋值过程。
frameworks/base/services/core/java/com/android/server/notification/ManagedServices.java
protected List<ManagedServiceInfo> getServices() {
synchronized (mMutex) {
List<ManagedServiceInfo> services = new ArrayList<>(mServices);
return services;
}
}
- 下面来看看mServices变量的初始化及赋值过程
搜索过滤发现,ManagedServices类中一共有两处向mService中add something的地方。
下面是一个倒序的过程
6.1 第一处
private void registerServiceLocked(final ComponentName name, final int userid,
final boolean isSystem) {
if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
final String servicesBindingTag = name.toString() + "/" + userid;
if (mServicesBinding.contains(servicesBindingTag)) {
// stop registering this thing already! we're working on it
return;
}
mServicesBinding.add(servicesBindingTag);
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
if (name.equals(info.component)
&& info.userid == userid) {
// cut old connections
if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": "
+ info.service);
removeServiceLocked(i);
if (info.connection != null) {
mContext.unbindService(info.connection);
}
}
}
Intent intent = new Intent(mConfig.serviceInterface);
intent.setComponent(name);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
final PendingIntent pendingIntent = PendingIntent.getActivity(
mContext, 0, new Intent(mConfig.settingsAction), 0);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
ApplicationInfo appInfo = null;
try {
appInfo = mContext.getPackageManager().getApplicationInfo(
name.getPackageName(), 0);
} catch (NameNotFoundException e) {
// Ignore if the package doesn't exist we won't be able to bind to the service.
}
final int targetSdkVersion =
appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
try {
if (DEBUG) Slog.v(TAG, "binding: " + intent);
ServiceConnection serviceConnection = new ServiceConnection() {
IInterface mService;
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
mServicesBinding.remove(servicesBindingTag);
try {
mService = asInterface(binder);
info = newServiceInfo(mService, name,
userid, isSystem, this, targetSdkVersion);
binder.linkToDeath(info, 0);
added = mServices.add(info);
} catch (RemoteException e) {
// already dead
}
}
if (added) {
onServiceAdded(info);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Slog.v(TAG, getCaption() + " connection lost: " + name);
}
};
if (!mContext.bindServiceAsUser(intent,
serviceConnection,
BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
new UserHandle(userid))) {
mServicesBinding.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
return;
}
} catch (SecurityException ex) {
Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
return;
}
}
public void registerSystemService(final ComponentName name, final int userid) {
synchronized (mMutex) {
registerServiceLocked(name, userid, true /* isSystem */);
}
}
6.2 第二处,registerService()和registerGuestService()是同级的,都是调用registerServiceImpl():
private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
synchronized (mMutex) {
try {
info.service.asBinder().linkToDeath(info, 0);
mServices.add(info);
return info;
} catch (RemoteException e) {
// already dead
}
}
return null;
}
public void registerService(IInterface service, ComponentName component, int userid) {
checkNotNull(service);
ManagedServiceInfo info = registerServiceImpl(service, component, userid);
if (info != null) {
onServiceAdded(info);
}
}
public void registerGuestService(ManagedServiceInfo guest) {
checkNotNull(guest.service);
if (!checkType(guest.service)) {
throw new IllegalArgumentException();
}
if (registerServiceImpl(guest) != null) {
onServiceAdded(guest);
}
}
再往下,就不能继续往下跟了。猜测应该是其它地方会调用这里的三个public方法中的一个,注册自己为这种类型的服务。
- 根据官方文档或经验,系统的默认notification listener service 是在SystemUI中实现的。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void start() {
... ...
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}
... ...
}
frameworks/base/core/java/android/service/notification/NotificationListenerService.java
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
if (mWrapper == null) {
mWrapper = new NotificationListenerWrapper();
}
mSystemContext = context;
INotificationManager noMan = getNotificationInterface();
mHandler = new MyHandler(context.getMainLooper());
mCurrentUser = currentUser;
noMan.registerListener(mWrapper, componentName, currentUser);
}
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
public void registerListener(final INotificationListener listener,
final ComponentName component, final int userid) {
enforceSystemOrSystemUI("INotificationManager.registerListener");
mListeners.registerService(listener, component, userid);
}
到这里,就看到了6.2的方法。
- 下面看一看notification的UI是如何显示出来的
接着4中,最后会调用notification listener service 的onNotificationPosted()方法。下面来看一下SystemUI的这个方法:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
if (sbn != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
processForRemoteInput(sbn.getNotification());
String key = sbn.getKey();
mKeysKeptForRemoteInput.remove(key);
boolean isUpdate = mNotificationData.get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since we're not going to show them
// anyway. This is true also when the summary is canceled,
// because children are automatically canceled by NoMan in that case.
if (!ENABLE_CHILD_NOTIFICATIONS
&& mGroupManager.isChildInGroupWithSummary(sbn)) {
if (DEBUG) {
Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
}
// 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);
}
}
});
}
}
public void addNotification(StatusBarNotification notification, RankingMap ranking)
throws InflationException {
String key = notification.getKey();
if (DEBUG) Log.d(TAG, "addNotification key=" + key);
mNotificationData.updateRanking(ranking);
Entry shadeEntry = createNotificationViews(notification);
boolean isHeadsUped = shouldPeek(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(key)) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
}
} else if (mNotificationData.getImportance(key)
< NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: not important enough: "
+ key);
}
} else {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// not immersive & a full-screen alert should be shown
if (DEBUG)
Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
key);
notification.getNotification().fullScreenIntent.send();
shadeEntry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
} catch (PendingIntent.CanceledException e) {
}
}
}
abortExistingInflation(key);
mForegroundServiceController.addNotification(notification,
mNotificationData.getImportance(key));
mPendingNotifications.put(key, shadeEntry);
}
protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
throws InflationException {
if (DEBUG) {
Log.d(TAG, "createNotificationViews(notification=" + sbn);
}
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;
}
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);
});
}
}
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;
// MStar Android Patch Begin
if (((sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE)) != null) || ((sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT)) != null)) {
final StatusBarIcon ic = new StatusBarIcon(
entry.notification.getUser(),
entry.notification.getPackageName(),
entry.notification.getNotification().getSmallIcon(),
entry.notification.getNotification().iconLevel,
entry.notification.getNotification().number,
entry.notification.getNotification().tickerText);
Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
final View popupNotificationView = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.notification_template_material_base,
null);
popupNotificationView.setBackgroundColor(0xffffffff);
final ImageView popupNotificationIcon = (ImageView) popupNotificationView.findViewById(
com.android.internal.R.id.icon);
final ImageView popupNotificationProfileBadge = (ImageView) popupNotificationView.findViewById(
com.android.internal.R.id.profile_badge);
popupNotificationIcon.setImageDrawable(iconDrawable);
if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
popupNotificationIcon.setBackgroundResource(
com.android.internal.R.drawable.notification_template_icon_bg);
int padding = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_large_icon_circle_padding);
popupNotificationIcon.setPadding(padding, padding, padding, padding);
if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
popupNotificationIcon.getBackground().setColorFilter(
sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
}
}
if (popupNotificationProfileBadge != null) {
Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
entry.notification.getUser(), 0);
if (profileDrawable != null) {
popupNotificationProfileBadge.setImageDrawable(profileDrawable);
popupNotificationProfileBadge.setVisibility(View.VISIBLE);
} else {
popupNotificationProfileBadge.setVisibility(View.GONE);
}
}
final TextView popupNotificationTitle = (TextView) popupNotificationView.findViewById(
com.android.internal.R.id.title);
final TextView popupNotificationText = (TextView) popupNotificationView.findViewById(
com.android.internal.R.id.text);
if (sbn.getNotification().extras != null) {
popupNotificationTitle.setText(sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE));
popupNotificationText.setText(sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT));
}
else if (sbn.getNotification().tickerText != null){
popupNotificationTitle.setText(sbn.getNotification().tickerText);
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.RGBA_8888);
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
mWindowManager.addView(popupNotificationView, lp);
mNotificationClicker.register(row, popupNotificationView, sbn);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mWindowManager.removeView(popupNotificationView);
}
}, 3000);
} else {
Log.e(TAG, "Notification String Title and Text is null ");
}
// MStar Android Patch End
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);
}
最终,可以知道,notification的通知UI是通过WindowManager添加的无焦点小窗口。
至此,整个过程完结。