2014年3月10日

Android display badge on icon (在應用程式圖示上顯示未讀訊息數字)

這幾天在開發android app時,突然想到android手機桌面上似乎不像iPhone上,app icon上會有未讀訊息的設計。經過爬文後,才知道原來目前好像只有Samgsung和Sony的手機有特定的寫法,可以讓支援app iconl來顯示未讀訊息數字。
雖然說是有支援Samsung的手持裝置,但好像比較早期的手機仍然無法支援。
1 第一種支援samsung手機的寫法:
ContentValues cv = new ContentValues(); 
cv.put("package", context.getPackageName());
cv.put("class", launcherClassName);
cv.put("badgecount ", count);
context.getContentResolver().insert(Uri.parse("content://com.sec.badge/apps"), cv);
count及icon上要顯示的數字,支援數字,如果傳入其他型態可能會掛掉。 如果想要把icon上的數字清除,就可以使用下可的code:
ContentValues cv = new ContentValues(); 
cv.put("badgecount ", 0);
getContentResolver().update(Uri.parse("content://com.sec.badge/apps"), cv, "package=?", new String[] {getPackageName()});
經過測試後,舊款的samsung手機,執行時就直接掛點了,看來似乎是不支援此語法。
2 第二種支援samsung手機的寫法:
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE" );
intent.putExtra( "badge_count", count);
intent.putExtra( "badge_count_package_name", context.getPackageName());
intent.putExtra( "badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
這種寫法感覺更簡潔方便,如果要清除icon上的數字,只要將count帶入0值就可以了。 測試後,發現舊款的samsung手機仍然不支援,但比較好的是它不會因此掛點了。
請注意,再測試時可能會發現沒有作用,這時請先檢查AndroidManifest.xml 是否有加入這兩行:
<uses-permission android:name= "com.sec.android.provider.badge.permission.READ" />
<uses-permission android:name= "com.sec.android.provider.badge.permission.WRITE" />
如果連加入這兩行還是沒有作用,那大概就可以確定不支援此寫法了。
3 支援Sony手機的寫法:
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE" );
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME",launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE" ,true);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE" ,count + "");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME",context.getPackageName());
context.sendBroadcast(intent);
count是顯示在icon上的數字,不過支援sony手機的寫法,竟然可以顯示文字,所以"com.sonyericsson.home.intent.extra.badge.MESSAGE"必須帶入字串。 以下則是取消icon上顯示文字的寫法,重點則是將"SHOW_MESSAGE",帶入false即可。
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE" );
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME",launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE" ,false);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME",context.getPackageName());
context.sendBroadcast(intent);
要讓Sony手機支援此寫法,先要檢查AndroidManifest.xml裡是否有加入這一行:
<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE" />
為了測試寫法有沒有效果,就寫了一段測試程式:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width= "match_parent"
    android:layout_height= "match_parent"
    android:paddingBottom= "@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight= "@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_marginTop= "20dp"
        android:id= "@+id/hello"
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        android:text= "請輸入數字"
        android:textSize= "18sp"
         />

    <EditText
        android:id= "@+id/unreadnumber"
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        android:text= "99"
        android:textSize= "18sp"
        android:layout_toRightOf= "@+id/hello"
        android:layout_alignBottom= "@+id/hello"
        android:inputType= "number"
        />

    <Button
        android:id= "@+id/btnSet"
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        android:text= "確定"
        android:layout_toRightOf= "@+id/unreadnumber"
        android:layout_alignBottom= "@+id/unreadnumber"/>

</RelativeLayout>
我加入一個可以輸入數字的edittext,並且附與按鈕可以將數字設定到icon上。
MainActivity.java
public class MainActivity extends Activity {

     private EditText et_number;
     private Button btnSet;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
           setContentView(R.layout. activity_main);

            et_number = (EditText) findViewById(R.id.unreadnumber );
            btnSet = (Button) findViewById(R.id. btnSet);

            btnSet.setOnClickListener( new View.OnClickListener() {
                 @Override
                 public void onClick(View view) {

                      setBadge(MainActivity.this,
                                Integer. parseInt(et_number.getText().toString()));
                }
           });
     }

     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
           // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu. main, menu);
           return true;
     }

     public static void setBadge(Context context, int count) {

           Log. d("mobile type" , android.os.Build.MANUFACTURER);

           String launcherClassName = getLauncherClassName(context);
            if (launcherClassName == null) {
                 return;
           }
           Log. d("launcherClassName" , launcherClassName);

            if (android.os.Build.MANUFACTURER .contains("samsung" )) {
                 // Samsung OK
                Intent intent = new Intent(
                           "android.intent.action.BADGE_COUNT_UPDATE" );
                intent.putExtra( "badge_count", count);
                intent.putExtra( "badge_count_package_name",
                           context.getPackageName());
                intent.putExtra( "badge_count_class_name", launcherClassName);
                context.sendBroadcast(intent);

           } else if (android.os.Build.MANUFACTURER .contains("Sony" )) {
                 // Sony OK
                Intent intent = new Intent();

                intent.setAction( "com.sonyericsson.home.action.UPDATE_BADGE" );
                intent.putExtra(
                           "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME" ,
                           launcherClassName);
                 if (count <= 0) {
                     intent.putExtra(
                                 "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE" ,
                                 false);
                } else {
                     intent.putExtra(
                                 "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE" ,
                                 true);
                     intent.putExtra(
                                 "com.sonyericsson.home.intent.extra.badge.MESSAGE" ,
                                count + ""); // 可顯示String
                }
                intent.putExtra(
                           "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME" ,
                           context.getPackageName());

                context.sendBroadcast(intent);

           } else {
                  }

     }

     public static String getLauncherClassName(Context context) {

           PackageManager pm = context.getPackageManager();

           Intent intent = new Intent(Intent. ACTION_MAIN);
           intent.addCategory(Intent. CATEGORY_LAUNCHER);

           List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
            for (ResolveInfo resolveInfo : resolveInfos) {
                String pkgName = resolveInfo.activityInfo.applicationInfo .packageName ;
                 if (pkgName.equalsIgnoreCase(context.getPackageName())) {
                     String className = resolveInfo.activityInfo.name ;
                      return className;
                }
           }
            return null;
     }
如何判斷手機的廠牌?
android.os.Build.MANUFACTURER .contains("samsung")
android.os.Build.MANUFACTURER .contains("Sony")
程式準備後,接著我們就可以把程式上到手機上去踹踹囉!
參考網站為:http://blog.xuite.net/kaymaner/Android/202439033