前几天修改MTK8312平台遇到一个bug,就是来电,短信,邮件或者日历有新消息时,桌面图标没有提示有几条信息,方便用户进行查看,其实MTK平台中是有这方面接口定义的,下面就让我们来查看一下源码吧(此案例主要是针对dialer做详细说明,其他应用需要更新的话,只需要定义接口和发送广播即可).
1、MTK中未读消息intent的定义,方便后续发送广播
所在目录:
frameworks\base\core\java\android\content\Intent.java
添加相关代码如下:
/**
* M:
* Broadcast Action: action used for launcher unread number feature.
* The broadcat is sent when the unread number of application changes.
* @hide
*/
public static final String MTK_ACTION_UNREAD_CHANGED = "com.mediatek.action.UNREAD_CHANGED";
/**
* M:
* Extra used to indicate the unread number of which component changes.
* @hide
*/
public static final String MTK_EXTRA_UNREAD_COMPONENT = "com.mediatek.intent.extra.UNREAD_COMPONENT";
/**
* M:
* The number of unread messages.
* @hide
*/
public static final String MTK_EXTRA_UNREAD_NUMBER = "com.mediatek.intent.extra.UNREAD_NUMBER";
2、查找未读信息并发送需要处理的广播所在位置
packages/apps/Calendar/src/com/mediatek/calendar/MTKUtils.java //日历
packages/apps/Email/src/com/android/email/NotificationController.java //邮件
packages/providers/ContactsProvider/src/com/android/providers/contacts/CallLogProvider.java //电话
packages/providers/TelephonyProvider/src/com/android/providers/telephony/MmsSmsProvider.java //短信
这里以dialer为例,找到上述目录后,添加如下代码:
public static final void notifyNewCallsCount(SQLiteDatabase db, Context context) {
Cursor c = null;
if (VvmUtils.isVvmEnabled()) {
c = db.rawQuery("select count(_id) from calls where type in (3,4) AND new=1", null);
} else {
c = db.rawQuery("select count(_id) from calls where type=3 AND new=1", null);
}
int newCallsCount = 0;
if (c != null) {
if (c.moveToFirst()) {
newCallsCount = c.getInt(0);
}
c.close();
}
LogUtils.i(TAG, "[notifyNewCallsCount] newCallsCount = " + newCallsCount);
//send count=0 to clear the unread icon
if (newCallsCount >= 0) {
Intent newIntent = new Intent(Intent.MTK_ACTION_UNREAD_CHANGED);
newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, newCallsCount);
newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT, new ComponentName(Constants.CONTACTS_PACKAGE,
Constants.CONTACTS_DIALTACTS_ACTIVITY));
context.sendBroadcast(newIntent);
android.provider.Settings.System.putInt(context.getContentResolver(), Constants.CONTACTS_UNREAD_KEY, Integer
.valueOf(newCallsCount));
}
}
3、广播的接收,需要自己在Launcher中 定义一个广播类,extends BroadcastReceiver,目录如下:
packages\apps\Launcher3\src\com\android\launcher3\MTKUnreadLoader.java
重写广播方法如下:
@Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (Intent.MTK_ACTION_UNREAD_CHANGED.equals(action)) {
final ComponentName componentName = (ComponentName) intent
.getExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT);
final int unreadNum = intent.getIntExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, -1);
if (LauncherLog.DEBUG) {
LauncherLog.d(TAG, "Receive unread broadcast: componentName = " + componentName
+ ", unreadNum = " + unreadNum + ", mCallbacks = " + mCallbacks
+ getUnreadSupportShortcutInfo());
}
if (mCallbacks != null && componentName != null && unreadNum != -1) {
final int index = supportUnreadFeature(componentName);
if (index >= 0) {
boolean ret = setUnreadNumberAt(index, unreadNum);
if (ret) {
final UnreadCallbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindComponentUnreadChanged(componentName, unreadNum);
}
}
}
}
}
}
4、注册广播所在位置,这里通过代码中动态注册广播
所在目录:packages\apps\Launcher3\src\com\android\launcher3\LauncherApplication.java
注册代码如下,在onCreate方法中添加:
if (FeatureOption.MTK_LAUNCHER_UNREAD_SUPPORT) {
mUnreadLoader = new MTKUnreadLoader(getApplicationContext());
// Register unread change broadcast.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.MTK_ACTION_UNREAD_CHANGED);
registerReceiver(mUnreadLoader, filter);
}
5、应用图标显示未读消息的的数字,通过在 MTKUnreadLoader.java中添加静态方法如下:
static void drawUnreadEventIfNeed(Canvas canvas, View icon) {
ItemInfo info = (ItemInfo)icon.getTag();
if (info != null && info.unreadNum > 0) {
Resources res = icon.getContext().getResources();
/// M: Meature sufficent width for unread text and background image
Paint unreadTextNumberPaint = new Paint();
unreadTextNumberPaint.setTextSize(res.getDimension(R.dimen.unread_text_number_size));
unreadTextNumberPaint.setTypeface(Typeface.DEFAULT_BOLD);
unreadTextNumberPaint.setColor(0xffffffff);
unreadTextNumberPaint.setTextAlign(Paint.Align.CENTER);
Paint unreadTextPlusPaint = new Paint(unreadTextNumberPaint);
unreadTextPlusPaint.setTextSize(res.getDimension(R.dimen.unread_text_plus_size));
String unreadTextNumber;
String unreadTextPlus = "+";
Rect unreadTextNumberBounds = new Rect(0, 0, 0, 0);
Rect unreadTextPlusBounds = new Rect(0, 0, 0, 0);
if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
unreadTextNumber = String.valueOf(Launcher.MAX_UNREAD_COUNT);
unreadTextPlusPaint.getTextBounds(unreadTextPlus, 0, unreadTextPlus.length(), unreadTextPlusBounds);
} else {
unreadTextNumber = String.valueOf(info.unreadNum);
}
unreadTextNumberPaint.getTextBounds(unreadTextNumber, 0, unreadTextNumber.length(), unreadTextNumberBounds);
int textHeight = unreadTextNumberBounds.height();
int textWidth = unreadTextNumberBounds.width() + unreadTextPlusBounds.width();
/// M: Draw unread background image.
NinePatchDrawable unreadBgNinePatchDrawable = (NinePatchDrawable) res.getDrawable(R.drawable.ic_newevents_numberindication);
int unreadBgWidth = unreadBgNinePatchDrawable.getIntrinsicWidth();
int unreadBgHeight = unreadBgNinePatchDrawable.getIntrinsicHeight();
int unreadMinWidth = (int) res.getDimension(R.dimen.unread_minWidth);
if (unreadBgWidth < unreadMinWidth) {
unreadBgWidth = unreadMinWidth;
}
int unreadTextMargin = (int) res.getDimension(R.dimen.unread_text_margin);
if (unreadBgWidth < textWidth + unreadTextMargin) {
unreadBgWidth = textWidth + unreadTextMargin;
}
if (unreadBgHeight < textHeight) {
unreadBgHeight = textHeight;
}
Rect unreadBgBounds = new Rect(0, 0, unreadBgWidth, unreadBgHeight);
unreadBgNinePatchDrawable.setBounds(unreadBgBounds);
int unreadMarginTop = 0;
int unreadMarginRight = 0;
if (info instanceof ShortcutInfo) {
if (info.container == (long) LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
unreadMarginTop = (int) res.getDimension(R.dimen.hotseat_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.hotseat_unread_margin_right);
} else if (info.container == (long) LauncherSettings.Favorites.CONTAINER_DESKTOP) {
unreadMarginTop = (int) res.getDimension(R.dimen.workspace_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.workspace_unread_margin_right);
} else {
unreadMarginTop = (int) res.getDimension(R.dimen.folder_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.folder_unread_margin_right);
}
} else if (info instanceof FolderInfo) {
if (info.container == (long) LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
unreadMarginTop = (int) res.getDimension(R.dimen.hotseat_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.hotseat_unread_margin_right);
} else if (info.container == (long) LauncherSettings.Favorites.CONTAINER_DESKTOP) {
unreadMarginTop = (int) res.getDimension(R.dimen.workspace_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.workspace_unread_margin_right);
}
}
else if (info instanceof AppInfo) {
unreadMarginTop = (int) res.getDimension(R.dimen.app_list_unread_margin_top);
unreadMarginRight = (int) res.getDimension(R.dimen.app_list_unread_margin_right);
}
int unreadBgPosX = icon.getScrollX() + icon.getWidth() - unreadBgWidth - unreadMarginRight;
int unreadBgPosY = icon.getScrollY() + unreadMarginTop;
canvas.save();
canvas.translate(unreadBgPosX, unreadBgPosY);
unreadBgNinePatchDrawable.draw(canvas);
/// M: Draw unread text.
Paint.FontMetrics fontMetrics = unreadTextNumberPaint.getFontMetrics();
if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
canvas.drawText(unreadTextNumber,
(unreadBgWidth - unreadTextPlusBounds.width()) / 2,
(unreadBgHeight + textHeight) / 2,
unreadTextNumberPaint);
canvas.drawText(unreadTextPlus,
(unreadBgWidth + unreadTextNumberBounds.width()) / 2,
(unreadBgHeight + textHeight) / 2 + fontMetrics.ascent / 2,
unreadTextPlusPaint);
} else {
canvas.drawText(unreadTextNumber,
unreadBgWidth / 2,
(unreadBgHeight + textHeight) / 2,
unreadTextNumberPaint);
}
canvas.restore();
}
}
7、定义未读消息所在的xml文件,这里支持电话,短信,邮件和日历
所在目录:packages\apps\Launcher3\res\xml\unread_support_shortcuts.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<unreadshortcuts xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher3">
<shortcut
launcher:unreadPackageName="com.android.dialer"
launcher:unreadClassName="com.android.dialer.DialtactsActivity"
launcher:unreadType="0"
launcher:unreadKey="com_android_contacts_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.android.mms"
launcher:unreadClassName="com.android.mms.ui.BootActivity"
launcher:unreadType="0"
launcher:unreadKey="com_android_mms_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.android.email"
launcher:unreadClassName="com.android.email.activity.Welcome"
launcher:unreadType="0"
launcher:unreadKey="com_android_email_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.android.calendar"
launcher:unreadClassName="com.android.calendar.AllInOneActivity"
launcher:unreadType="0"
launcher:unreadKey="com_android_calendar_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.orangelabs.rcs"
launcher:unreadClassName="com.mediatek.rcse.activities.ChatMainActivity"
launcher:unreadType="0"
launcher:unreadKey="com_android_rcse_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.android.cellbroadcastreceiver"
launcher:unreadClassName="com.android.cellbroadcastreceiver.CellBroadcastListActivity"
launcher:unreadType="0"
launcher:unreadKey="com_android_calendar_mtk_unread"
/>
<shortcut
launcher:unreadPackageName="com.mediatek.cellbroadcastreceiver"
launcher:unreadClassName="com.mediatek.cellbroadcastreceiver.CellBroadcastListActivity"
launcher:unreadType="0"
launcher:unreadKey="com_mediatek_cellbroadcastreceiver_mtk_unread"
/>
</unreadshortcuts>
8、事件的图标显示,可以根据尺寸,做一张.9图片,eg:
ic_newevents_numberindication.9.png
9、MTKUnreadLoader.java中定义一些接口回调方法,供Launcher3实现和调用
public interface UnreadCallbacks {
/**
* Bind shortcuts and application icons with the given component, and
* update folders unread which contains the given component.
*
* @param component
* @param unreadNum
*/
void bindComponentUnreadChanged(ComponentName component, int unreadNum);
/**
* Bind unread shortcut information if needed, this call back is used to
* update shortcuts and folders when launcher first created.
*/
void bindUnreadInfoIfNeeded();
}
10、 Launcher3.java实现 UnreadCallbacks 方法
public void bindComponentUnreadChanged(final ComponentName component, final int unreadNum) {
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "bindComponentUnreadChanged: component = " + component
+ ", unreadNum = " + unreadNum + ", this = " + this);
}
// Post to message queue to avoid possible ANR.
mHandler.post(new Runnable() {
public void run() {
final long start = System.currentTimeMillis();
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindComponentUnreadChanged begin: component = " + component
+ ", unreadNum = " + unreadNum + ", start = " + start);
}
if (mWorkspace != null) {
mWorkspace.updateComponentUnreadChanged(component, unreadNum);
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.updateAppsUnreadChanged(component, unreadNum);
}
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindComponentUnreadChanged end: current time = "
+ System.currentTimeMillis() + ", time used = "
+ (System.currentTimeMillis() - start));
}
}
});
}
/**
* M: Bind shortcuts unread number if binding process has finished.
*/
public void bindUnreadInfoIfNeeded() {
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "bindUnreadInfoIfNeeded: mBindingWorkspaceFinished = "
+ mBindingWorkspaceFinished + ", thread = " + Thread.currentThread());
}
if (mBindingWorkspaceFinished) {
bindWorkspaceUnreadInfo();
}
if (mBindingAppsFinished) {
bindAppsUnreadInfo();
}
mUnreadLoadCompleted = true;
}
/**
* M: Bind unread number to shortcuts with data in MTKUnreadLoader.
*/
private void bindWorkspaceUnreadInfo() {
mHandler.post(new Runnable() {
public void run() {
final long start = System.currentTimeMillis();
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindWorkspaceUnreadInfo begin: start = " + start);
}
if (mWorkspace != null) {
mWorkspace.updateShortcutsAndFoldersUnread();
}
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindWorkspaceUnreadInfo end: current time = "
+ System.currentTimeMillis() + ",time used = "
+ (System.currentTimeMillis() - start));
}
}
});
}
/**
* M: Bind unread number to shortcuts with data in MTKUnreadLoader.
*/
private void bindAppsUnreadInfo() {
mHandler.post(new Runnable() {
public void run() {
final long start = System.currentTimeMillis();
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindAppsUnreadInfo begin: start = " + start);
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.updateAppsUnread();
}
if (LauncherLog.DEBUG_PERFORMANCE) {
LauncherLog.d(TAG, "bindAppsUnreadInfo end: current time = "
+ System.currentTimeMillis() + ",time used = "
+ (System.currentTimeMillis() - start));
}
}
});
}
11、Workspace.java中进行icon更新的方法
public void updateShortcutsAndFoldersUnread() {
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "updateShortcutsAndFolderUnread: this = " + this);
}
final ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
int childCount = 0;
View view = null;
Object tag = null;
for (ShortcutAndWidgetContainer layout : childrenLayouts) {
childCount = layout.getChildCount();
for (int j = 0; j < childCount; j++) {
view = layout.getChildAt(j);
tag = view.getTag();
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "updateShortcutsAndFoldersUnread: tag = " + tag + ", j = "
+ j + ", view = " + view);
}
if (tag instanceof ShortcutInfo) {
final ShortcutInfo info = (ShortcutInfo) tag;
final Intent intent = info.intent;
final ComponentName componentName = intent.getComponent();
info.unreadNum = MTKUnreadLoader.getUnreadNumberOfComponent(componentName);
((BubbleTextView) view).invalidate();
} else if (tag instanceof FolderInfo) {
((FolderIcon) view).updateFolderUnreadNum();
((FolderIcon) view).invalidate();
}
}
}
}
/**
* M: Update unread number of shortcuts and folders in workspace and hotseat
* with the given component.
*
* @param component
* @param unreadNum
*/
public void updateComponentUnreadChanged(ComponentName component, int unreadNum) {
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "updateComponentUnreadChanged: component = " + component
+ ", unreadNum = " + unreadNum);
}
final ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
int childCount = 0;
View view = null;
Object tag = null;
for (ShortcutAndWidgetContainer layout : childrenLayouts) {
childCount = layout.getChildCount();
for (int j = 0; j < childCount; j++) {
view = layout.getChildAt(j);
tag = view.getTag();
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "updateComponentUnreadChanged: component = " + component
+ ",tag = " + tag + ",j = " + j + ",view = " + view);
}
if (tag instanceof ShortcutInfo) {
final ShortcutInfo info = (ShortcutInfo) tag;
final Intent intent = info.intent;
final ComponentName componentName = intent.getComponent();
if (LauncherLog.DEBUG_UNREAD) {
LauncherLog.d(TAG, "updateComponentUnreadChanged 2: find component = "
+ component + ",intent = " + intent + ",componentName = " + componentName);
}
if (componentName != null && componentName.equals(component)) {
LauncherLog.d(TAG, "updateComponentUnreadChanged 1: find component = "
+ component + ",tag = " + tag + ",j = " + j + ",cellX = "
+ info.cellX + ",cellY = " + info.cellY);
info.unreadNum = unreadNum;
((BubbleTextView) view).invalidate();
}
} else if (tag instanceof FolderInfo) {
((FolderIcon) view).updateFolderUnreadNum(component, unreadNum);
((FolderIcon) view).invalidate();
}
}
}
/// M: Update shortcut within folder if open folder exists.
Folder openFolder = getOpenFolder();
if (openFolder != null) {
openFolder.updateContentUnreadNum();
}
注意事项:对于shortcut,folder 和hotseat,更新的代码可能不一样,最后都记得调用 MTKUnreadLoader中的drawUnreadEventIfNeed方法
具体:在各自的onDraw方法中添加MTKUnreadLoader.drawUnreadEventIfNeed(canvas,this).
eg: FolderIcon.java在dispatchDraw()方法中添加
BubbleTextView.java在draw()方法中添加