Android案頭捷徑那些事與那些坑

來源:互聯網
上載者:User

標籤:android   捷徑   shortcut   

原文來自http://blog.zanlabs.com/2015/03/14/android-shortcut-summary/

將近二個多月沒寫部落格了。
之前一段時間一直在搞紅包助手,就沒抽時間寫部落格,但寫這個真的是很好玩。沒想到居然在Android上實現類比點擊,從而實現自動搶紅包,有興趣的同學可以參考https://github.com/waylife/RedEnvelopeAssistant ,代碼已經開源。
紅包助手還有一些問題,但是現在基本的搶紅包基本沒問題了。目前正在對它進行最佳化以及較低版本的一些適配,還有項目的國際化工作。
廢話不多說了,下面是Andrioid開發過程中捷徑相關的事與坑。
資料來源於上次組內自己的CodeReview總結。

背景

一般情況下,為了讓使用者更方便的開啟應用,程式會在案頭上產生一些捷徑。
本來呢,如果是原生的案頭,其實是十分簡單,直接調用系統相關的API就行了。但是眾多的系統廠商以及眾多第三方自己定製的案頭(Launcher),導致在適配、相容方面存在很多問題。
比如,有些案頭無法刪除捷徑(比如小米),有些案頭無法產生捷徑(比如鎚子),有些系統無法更新案頭表徵圖(比如華為榮耀6)。
在升級、降級的時候捷徑發生變化;比如,全部變成應用的主表徵圖,升級、降級後點擊捷徑沒有反應,刪除應用後無法刪除捷徑。
很多問題都是需要解決的,雖然有些由於系統限制,沒有辦法搞定所有的,但是仍然需要尋求一個最優的方案。這也就是本文需要討論的問題。
本文說指的捷徑是指應用案頭捷徑,不包含長按彈出的產生捷徑。
捷徑所有資訊都是存在於launcher的favorite表。一般需要用到的欄位為_id,title,intent,iconResource,icon,分別表示 捷徑名稱,捷徑intent,捷徑表徵圖(本地),捷徑表徵圖(data二進位壓縮資料)。

兩個intent資料如下

資料可以通過SQLite Editor查看,需要已經ROOT的手機

實現增加捷徑

在AndroidManifest.xml增加許可權

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />

同時,根據Intent是隱式還是顯示在相關的Activity聲明相關的intent-filter。
相關代碼:

刪除捷徑

跟增加捷徑一樣,也是需要增加許可權的。加上

<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />

相關代碼:

捷徑修改

需要增加許可權

<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /><uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
如果適配所有案頭,請添加附錄中第二條所列出的許可權。
系統並沒有提供API去更改案頭捷徑。只能通過其他猥瑣的辦法了,可行的的辦法之一就是通過ContentProvider去更改資料庫相關的資訊。當然有人會說了,先刪掉捷徑,再重新建立不就行了?這是個辦法。但是有些系統是無法刪除捷徑的;另外,刪除捷徑與建立捷徑都是通過廣播實現的,這個地方需要控制兩者的時間間隔。權衡之後,選用第一種辦法相對穩妥。
廢話不多少,上代碼。

/**  * 更新案頭捷徑表徵圖,不一定所有表徵圖都有效<br/>  * 如果捷徑不存在,則不更新<br/>.  */ public static void updateShortcutIcon(Context context, String title, Intent intent,Bitmap bitmap) {  if(bitmap==null){   XLog.i(TAG, "update shortcut icon,bitmap empty");   return;  }  try{   final ContentResolver cr = context.getContentResolver();   StringBuilder uriStr = new StringBuilder();   String urlTemp="";   String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);   if(authority==null||authority.trim().equals("")){    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");   }   uriStr.append("content://");   if (TextUtils.isEmpty(authority)) {    int sdkInt = android.os.Build.VERSION.SDK_INT;    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的     uriStr.append("com.android.launcher.settings");    } else if (sdkInt < 19) {// Android 4.4以下     uriStr.append("com.android.launcher2.settings");    } else {// 4.4以及以上     uriStr.append("com.android.launcher3.settings");    }   } else {    uriStr.append(authority);   }   urlTemp=uriStr.toString();   uriStr.append("/favorites?notify=true");   Uri uri = Uri.parse(uriStr.toString());   Cursor c = cr.query(uri, new String[] {"_id", "title", "intent" },     "title=?  and intent=? ",     new String[] { title, intent.toUri(0) }, null);   int index=-1;   if (c != null && c.getCount() > 0) {    c.moveToFirst();    index=c.getInt(0);//獲得表徵圖索引    ContentValues cv=new ContentValues();    cv.put("icon", flattenBitmap(bitmap));    Uri uri2=Uri.parse(urlTemp+"/favorites/"+index+"?notify=true");    int i=context.getContentResolver().update(uri2, cv, null,null);    context.getContentResolver().notifyChange(uri,null);//此處不能用uri2,是個坑    XLog.i(TAG, "update ok: affected "+i+" rows,index is"+index);   }else{    XLog.i(TAG, "update result failed");   }   if (c != null && !c.isClosed()) {    c.close();   }  }catch(Exception ex){   ex.printStackTrace();   XLog.i(TAG, "update shortcut icon,get errors:"+ex.getMessage());  } } private static byte[] flattenBitmap(Bitmap bitmap) {  // Try go guesstimate how much space the icon will take when serialized  // to avoid unnecessary allocations/copies during the write.  int size = bitmap.getWidth() * bitmap.getHeight() * 4;  ByteArrayOutputStream out = new ByteArrayOutputStream(size);  try {   bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);   out.flush();   out.close();   return out.toByteArray();  } catch (IOException e) {   XLog.w(TAG, "Could not write icon");   return null;  } }

捷徑存在判斷

需要增加的許可權同修改捷徑
雖然說通過SharePreference來保證捷徑不會重複建立,以及通過shortcutIntent.putExtra(“duplicate”, false)也可以確保,但是為了萬無一失,還是可以通過去查詢資料判斷捷徑是否存在,來避免重複建立。 代碼如下:

/**  * 檢查捷徑是否存在 <br/>  * <font color=red>注意:</font> 有些手機無法判斷是否已經建立過捷徑<br/>  * 因此,在建立捷徑時,請添加<br/>  * shortcutIntent.putExtra("duplicate", false);// 不允許重複建立<br/>  * 最好使用{@link #isShortCutExist(Context, String, Intent)}  * 進行判斷,因為可能有些應用產生的捷徑名稱是一樣的的<br/>  * 此處需要在AndroidManifest.xml中配置相關的案頭許可權資訊<br/>  * 錯誤資訊已捕獲<br/>  */ public static boolean isShortCutExist(Context context, String title) {  boolean result = false;  try {   final ContentResolver cr = context.getContentResolver();   StringBuilder uriStr = new StringBuilder();   String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);   if(authority==null||authority.trim().equals("")){    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");   }   uriStr.append("content://");   if (TextUtils.isEmpty(authority)) {    int sdkInt = android.os.Build.VERSION.SDK_INT;    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的     uriStr.append("com.android.launcher.settings");    } else if (sdkInt < 19) {// Android 4.4以下     uriStr.append("com.android.launcher2.settings");    } else {// 4.4以及以上     uriStr.append("com.android.launcher3.settings");    }   } else {    uriStr.append(authority);   }   uriStr.append("/favorites?notify=true");   Uri uri = Uri.parse(uriStr.toString());   Cursor c = cr.query(uri, new String[] { "title" },     "title=? ",     new String[] { title }, null);   if (c != null && c.getCount() > 0) {    result = true;   }   if (c != null && !c.isClosed()) {    c.close();   }  } catch (Exception e) {   e.printStackTrace();   result=false;  }  return result; } /**  * 不一定所有的手機都有效,因為國內大部分手機的案頭不是系統原生的<br/>  * 更多請參考{@link #isShortCutExist(Context, String)}<br/>  * 案頭有兩種,系統案頭(ROM內建)與第三方案頭,一般只考慮系統內建<br/>  * 第三方案頭如果沒有實現系統響應的方法是無法判斷的,比如GO案頭<br/>  * 此處需要在AndroidManifest.xml中配置相關的案頭許可權資訊<br/>  * 錯誤資訊已捕獲<br/>  */ public static boolean isShortCutExist(Context context, String title, Intent intent) {  boolean result = false;  try{   final ContentResolver cr = context.getContentResolver();   StringBuilder uriStr = new StringBuilder();   String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);   if(authority==null||authority.trim().equals("")){    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");   }   uriStr.append("content://");   if (TextUtils.isEmpty(authority)) {    int sdkInt = android.os.Build.VERSION.SDK_INT;    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的     uriStr.append("com.android.launcher.settings");    } else if (sdkInt < 19) {// Android 4.4以下     uriStr.append("com.android.launcher2.settings");    } else {// 4.4以及以上     uriStr.append("com.android.launcher3.settings");    }   } else {    uriStr.append(authority);   }   uriStr.append("/favorites?notify=true");   Uri uri = Uri.parse(uriStr.toString());   Cursor c = cr.query(uri, new String[] { "title", "intent" },     "title=?  and intent=?",     new String[] { title, intent.toUri(0) }, null);   if (c != null && c.getCount() > 0) {    result = true;   }   if (c != null && !c.isClosed()) {    c.close();   }  }catch(Exception ex){   result=false;   ex.printStackTrace();  }  return result; }

相容與注意事項相容

所有的捷徑Intent如果不是之前版本的存在很大問題,絕對不要改變參數,否則升級或者降級時捷徑會出現問題;
同時,儘可能的採用隱式調用,自訂CATEGORY,而不是自訂ACTION,ACTION參數一定要為ACTION_MAIN,否則有些手機在卸載時無法刪除捷徑(WTF)。

注意事項
  • 【所有】activity路徑的變更導致老版本升級之後捷徑無法使用
    —> 1.一旦使用確定了activity的包路徑,之後就不要再變更;
    —> 2.儘可能使用隱式調用,但是如果之前已經發出去的版本,為了相容性,就必須一直使用老的方式,新版本的儘可能的不要更改方式,如果使用者降級,老版本捷徑會無法使用。
  • 【部分】多個捷徑指向一個activity導致部分手機(三星SII)升級時表徵圖變成應用表徵圖
    —> 儘可能的避免多個捷徑指向同一個activity,可能通過多個activity再跳轉過去
  • 【部分】應用刪除時無法刪除捷徑。與系統案頭Launcher實現有關。
    —> 為了適配所有Launcher,Intent Action使用Intent.ACTION_MAIN。如果是隱式調用,儘可能自訂CATEGORY,而不是自訂ACTION。
  • 【部分】應用升級時需要刪除老版本部分捷徑,但是部分手機無法刪除
    —> 無解
  • 【部分】第三方案頭無法產生、刪除、更新捷徑
    —> 呵呵,一般來說產生沒有問題,但是刪除,更新大部分案頭會有問題。儘可能避免這些操作。或者專門適配該案頭,成本較高。
  • 【部分】部分案頭無法即時更新表徵圖,需要重啟
    —> 無解,嘗試過重啟Launcher,但是結果是之前的捷徑也消失了。只有重啟手機,按理來說應該是有方式觸發Launcher進行重新整理的。
    以上【所有】【部分】,分別表示必定出現,部分出現。
參考
  1. http://grepcode.com/search/?query=InstallShortcutReceiver
  2. http://developer.android.com/index.html
附錄
  1. 完整代碼可參考https://gist.github.com/waylife/437a3d98a84f245b9582
  2. 通用更新捷徑許可權列表
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.android.launcher2.permission.READ_SETTINGS" />    <uses-permission android:name="com.android.launcher2.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />    <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />    <uses-permission android:name="org.adw.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="org.adw.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.htc.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.qihoo360.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.qihoo360.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.lge.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.lge.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="net.qihoo.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="net.qihoo.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="org.adwfreak.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="org.adwfreak.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="org.adw.launcher_donut.permission.READ_SETTINGS" />    <uses-permission android:name="org.adw.launcher_donut.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.huawei.launcher3.permission.READ_SETTINGS" />    <uses-permission android:name="com.huawei.launcher3.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.fede.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.fede.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.sec.android.app.twlauncher.settings.READ_SETTINGS" />    <uses-permission android:name="com.sec.android.app.twlauncher.settings.WRITE_SETTINGS" />    <uses-permission android:name="com.anddoes.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.anddoes.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.tencent.qqlauncher.permission.READ_SETTINGS" />    <uses-permission android:name="com.tencent.qqlauncher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.huawei.launcher2.permission.READ_SETTINGS" />    <uses-permission android:name="com.huawei.launcher2.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.android.mylauncher.permission.READ_SETTINGS" />    <uses-permission android:name="com.android.mylauncher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.ebproductions.android.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.ebproductions.android.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />    <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />    <uses-permission android:name="telecom.mdesk.permission.READ_SETTINGS" />    <uses-permission android:name="telecom.mdesk.permission.WRITE_SETTINGS" />    <uses-permission android:name="dianxin.permission.ACCESS_LAUNCHER_DATA" />




Android案頭捷徑那些事與那些坑

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.