本文已獨家授權(quán) 郭霖 ( guolin_blog) 公眾號發(fā)布凭舶!
申明痢甘,標(biāo)題里的快捷方式不是指開發(fā)人員使用頻率極高的Ctrl+C和Ctrl+V馍刮;也不是IDE里Ctrl+D宪拥、Ctrl+F等常用快捷鍵仿野。這里的快捷鍵,是Android應(yīng)用生成桌面快捷方式她君。
試想脚作,有一Windows用戶想進入D盤——my文件夾里面的子文件去找文件(因藏了些晦澀資源所以層級較深)。那么這位少俠更加便利省力的操作是:點擊選中文件夾——右鍵:發(fā)送到——桌面快捷方式缔刹,即可幫他將快捷方式生成到桌面球涛。該用戶下次想使用這個文件夾,直接點擊桌面上的快捷方式即可校镐。好處在于亿扁,用戶可以快速定位到某一應(yīng)用具體的功能、干凈利落鸟廓。
當(dāng)然从祝,谷歌Android團隊也考慮了這一點,給我們設(shè)計了原生API引谜,方便我們開發(fā)人員更加便利的(Ctrl+C牍陌、V)生成桌面快捷方式。這樣做的好處我想有以下幾點员咽,首先毒涧,提高了用戶留存率,試想一個APP通過某種媒介生成了2個icon贝室,這樣是很容易吸引人的契讲,因為生成桌面快捷方式的icon以及點擊事件都是代碼可控的,比如你的快捷方式的icon是一個蘿莉或者御姐档玻;正太或是直男怀泊?畢竟圖片總有人會喜歡的嘛。其次误趴,快捷方式的點擊事件是控制的霹琼,跳轉(zhuǎn)的界面控制在開發(fā)者(產(chǎn)品)手中等等。
言歸正傳凉当,既然是生成桌面快捷方式枣申,那么肯定需要權(quán)限,必要的權(quán)限如下:
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<!-- 添加快捷方式 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- 移除快捷方式 -->
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
<!-- 查詢快捷方式 -->
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
接著看杭,因為Android難以言表的碎片化和廠商定制忠藤,所以還需要加一些權(quán)限來增加健壯性,下面直接copy就行:
<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" />
好了楼雹,權(quán)限已經(jīng)添加完畢模孩,下面就可以上代碼了尖阔,首先是創(chuàng)建桌面快捷方式:
//創(chuàng)建桌面快捷方式
private void createShortCut(){
//創(chuàng)建Intent對象
Intent shortcutIntent = new Intent();
//設(shè)置點擊快捷方式,進入指定的Activity
//注意:因為是從Lanucher中啟動榨咐,所以這里用到了ComponentName
//其中new ComponentName這里的第二個參數(shù)介却,是Activity的全路徑名,也就是包名類名要寫全块茁。
shortcutIntent.setComponent(new ComponentName(this.getPackageName(), "這里是包名.類名"));
//給Intent添加 對應(yīng)的flag
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|Intent.FLAG_ACTIVITY_NEW_TASK);
Intent resultIntent = new Intent();
// Intent.ShortcutIconResource.fromContext 這個就是設(shè)置快捷方式的圖標(biāo)
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this,
R.drawable.yuanbao));
//啟動的Intent
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
//這里可以設(shè)置快捷方式的名稱
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "快捷名稱");
//設(shè)置Action
resultIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
//發(fā)送廣播齿坷、通知系統(tǒng)創(chuàng)建桌面快捷方式
sendBroadcast(resultIntent);
}
創(chuàng)建桌面快捷方式的代碼,理論上就是上面這些数焊,ComponentName這個類用的較少永淌,簡單理解ComponentName的作用是,可以啟動其他應(yīng)用的Activity佩耳、Service(前提是要知道包名)遂蛀,然后搭配Intent使用,完成跳轉(zhuǎn)蚕愤。關(guān)于ComponentName與Activity答恶、Service的參考代碼如下:
ComponentName componentName = new ComponentName(param1,param2);
param1:Activity、Service 所在應(yīng)用的包名
//獲取應(yīng)用的包名可以通過 this.getPackageName(); this代表當(dāng)前的Activity
param2:Activity萍诱、Service的包名+類名
//這里是全路徑名:對應(yīng)的就是 this.getPackageName()+"YourActivity"
//this.getPackageName()+"YourService"
//ComponentName結(jié)合Activity的寫法如下:
ComponentName componentName = new ComponentName(this.getPackageName(), this.getPackageName()+"YourActivity");
Intent intent =new Intent();
intent.setComponent(componentName);
startActivity(intent);
//ComponentName結(jié)合Service的寫法如下:
ComponentName componentName = new ComponentName(this.getPackageName(), this.getPackageName()+"YourService");
Intent intent = new Intent();
intent.setComponent(componentName);
startService(intent);
另外悬嗓,還需要在清單配置文件里面,對應(yīng)的Activity和Service需要加上android:exported = "true"裕坊,這個android:exported標(biāo)簽包竹,是用來指示該服務(wù)是否能夠被其他應(yīng)用程序組件調(diào)用或跟它交互。設(shè)置成true籍凝,則能夠被調(diào)用或交互周瞎;設(shè)置false,也就意味不能被其他組件交互饵蒂。APP入口的Activity不聲明默認(rèn)就是android:exported="true"声诸。關(guān)于ComponentName的說明就到此為止,還不是很好理解的可以自行谷歌百度退盯。綜上彼乌,還需要在清單文件配置一些代碼,參考如下:
<activity
android:exported="true"
android:excludeFromRecents="true"
android:name=".TestCreatIconActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"
<action android:name="test.intent.action.SHORTCUT" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- 必須加上這個渊迁。否則無法直接使用自定的action -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:excludeFromRecents="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
這里的MainActivity慰照,就是我點擊快捷方式進入的Activity×鹦啵可能你會說毒租,我該如何傳值給,快捷方式點進去的Activity箱叁?那么也是傳統(tǒng)套路根據(jù)Intent來傳值墅垮,也就是上面的shortcutIntent惕医,參考代碼如下:
//傳參到指定界面,通過標(biāo)識和具體數(shù)據(jù)執(zhí)行
shortcutIntent.putExtra("key1","xxx");
shortcutIntent.putExtra("key2",true);
shortcutIntent.putExtra("key3",233);
...
好了噩斟,創(chuàng)建快捷方式的代碼是通過sendBroadcast(resultIntent);來實現(xiàn)的曹锨,因此可以判斷這是系統(tǒng)的廣播孤个。
刪除桌面快捷方式:
首先剃允,用戶可以直接在手機桌面拖拽快捷方式,進行刪除
另外齐鲤,上面的代碼也實現(xiàn)了當(dāng)刪除APK以后斥废,自動刪除快捷方式(因為你跳轉(zhuǎn)沒有了目標(biāo),所以需要刪除)由于代碼刪除快捷方式给郊,網(wǎng)上的一些代碼都有問題(又是令人無語的碎片化和廠商定制)牡肉,所以這里不提供刪除快捷方式的代碼,理論上淆九,上面2種手動刪除實現(xiàn)即可统锤。
值得注意的是,如果用戶沒有手動打開權(quán)限炭庙,也會創(chuàng)建失敗饲窿,也需要提示用戶手動對應(yīng)用進行快捷方式權(quán)限授權(quán)設(shè)置。
Android 7.1系統(tǒng)快捷方式的變化:
從Android 7.1(API 25焕蹄,也就是 minSdkVersion 25 )開始逾雄,新增了ShortcutManager,ShortcutManager腻脏,顧名思義鸦泳,翻譯過來就是快捷方式管理。
這個類可以對桌面久按應(yīng)用圖標(biāo)彈出的快捷方式進行管理永品。
那么ShortcutManager的實現(xiàn)方式有2種:
第一種:XML注冊
首先, 需要在res/xml目錄下創(chuàng)建一個新的xml文件,根標(biāo)簽是shortcuts做鹰,參考代碼如下:
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="setting"
android:enabled="true"
android:icon="@drawable/yuanbao"
android:shortcutShortLabel="@string/set_short_name"
android:shortcutLongLabel="@string/set_long_name"
android:shortcutDisabledMessage="@string/set_disable_msg">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="com.share.shortcut"
android:targetClass="com.share.shortcut.TargetActivity" />
<categories android:name="android.shortcut.conversation"/>
</shortcut>
</shortcuts>
嗯,可以看到新增了很多標(biāo)簽鼎姐,這些標(biāo)簽代表什么意思咧钾麸?首先是shortcut的外部標(biāo)簽:
shortcutId, 快捷方式的id
enabled:表示這個shortcut是否可用,一般設(shè)置為true即可
shortcutShortLabel:配置快捷方式的短名稱, 如果長名稱顯示不下, 就顯示短名稱
shortcutLongLabel: 配置快捷方式的長名稱, launcher會優(yōu)先選擇長名稱顯示(優(yōu)先級高于shortcutShortLabel)
shortcutDisabledMessage:這個標(biāo)簽的意思是指:當(dāng)我們點擊一個不可用的shortcut時症见,給用戶一個有效提示
內(nèi)部還有個intent標(biāo)簽:
android:action喂走,這里的action需要配置,否則會崩潰谋作,默認(rèn)寫法是android.intent.action.VIEW
targetPackage:目標(biāo)應(yīng)用的包名,
targetClass:點擊快捷方式要跳轉(zhuǎn)的目標(biāo)類, 這里要注意的是android:action一定要配置, 否則會崩潰
categories:這個標(biāo)簽谷歌團隊僅提供了android.shortcut.conversation 這一種芋肠,所以直接復(fù)制即可
好了,現(xiàn)在通過XML已經(jīng)寫好了快捷方式遵蚜,那現(xiàn)在該如何使用帖池,讓快捷方式生效奈惑?
那么,只需要在清單文件中啟動Activity的里面睡汹,加入以下2行代碼(也就是通過meta-data配置進去)即可看到效果:
<activity
android:launchMode="singleInstance"
android:name=".MainActivity"
android:excludeFromRecents="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!--<action android:name="test.intent.action.SHORTCUT" />-->
<category android:name="android.intent.category.LAUNCHER" />
<!-- 必須加上這個肴甸。否則無法直接使用自定的action -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
靜態(tài)注冊
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcut"/>
</activity>
這里的meta-data就是配置了桌面快捷方式的一些信息。代碼運行后囚巴,執(zhí)行創(chuàng)建快捷方式的操作即可看到效果:
說完了第一種XML注冊在來說第二種注冊:代碼動態(tài)注冊
首先原在,獲取系統(tǒng)服務(wù),固定寫法:getSystemService(ShortcutManager.class);獲取ShortcutManager類對象
接著彤叉,獲取完ShortcutManager對象以后庶柿,通過setDynamicShortcuts( List<ShortcutInfo> )方法去設(shè)置Shortcut快捷方式具體的名字、icon以及邏輯秽浇。
既然浮庐,現(xiàn)在要通過ShortcutInfo去完成信息填充,那么我們就先看下ShortcutInfo的基本寫法:
//獲取系統(tǒng)服務(wù)得到ShortcutManager對象
ShortcutManager systemService = getSystemService(ShortcutManager.class);
if (Build.VERSION.SDK_INT >= 25) {
//設(shè)置Intent跳轉(zhuǎn)邏輯
Intent intent = new Intent(ShortCut7_0Activity.this, TargetActivity.class);
intent.setAction(Intent.ACTION_VIEW);
//設(shè)置ID
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(this, "onlyId")
//設(shè)置短標(biāo)題
.setShortLabel("雅蠛蝶")
//設(shè)置長標(biāo)題
.setLongLabel("江山留勝跡")
//設(shè)置icon
.setIcon(Icon.createWithResource(this, R.drawable.yuanbao))
//設(shè)置Intent
.setIntent(intent)
.build();
//這樣就可以通過長按圖標(biāo)顯示出快捷方式了
systemService.setDynamicShortcuts(Arrays.asList(shortcutInfo));
以上代碼就可以創(chuàng)建桌面快捷方式柬焕,通過ShortcutInfo的Builder寫法可以動態(tài)的插入快捷方式的名稱审残、icon、以及Intent邏輯斑举。值得一提的是搅轿,ShortcutManager最大可以創(chuàng)建5個,但桌面上只能最多只能顯示4個快捷方式懂昂,可能是谷歌團隊基于何種情況的考慮進行的限制介时,這里就不做深入研究。另外凌彬,快捷方式的順序排列是:前面添加的顯示在下方沸柔,后面添加的顯示在上面。
另外铲敛,移除快捷方式可以調(diào)用如下API:
void removeDynamicShortcuts(@NonNull List shortcutIds);
Android 8.0系統(tǒng)快捷方式的變化:
可能是谷歌出新版本需要優(yōu)化增加既有版本的一些功能從Android 8.0(API 26褐澎,也就是 minSdkVersion 26,也就是Android O )開始伐蒋,對ShortcutManager這個類新增了更多的管理措施工三。
那么O系統(tǒng)主要的擴充說明有那些?我這里就簡單說明下先鱼,
一:
APP可以使用requestPinShortcut(ShortcutInfo俭正,IntentSender)將現(xiàn)有的快捷方式(靜態(tài)或動態(tài))或全新的快捷方式固定到支持的啟動器。這倆參數(shù)的意思是:
ShortcutInfo對象 - 如果快捷方式已存在焙畔,則該對象僅包含快捷方式的ID掸读。如果快捷方式不存在,新的ShortcutInfo對象必須包含新快捷方式的ID,意圖和短標(biāo)簽儿惫。
PendingIntent對象 - 此意圖表示如果快捷方式成功固定到設(shè)備的啟動器澡罚,您的應(yīng)用程序?qū)⑹盏交卣{(diào)。
分析:也就是說肾请,谷歌要求新的快捷方式需要編寫的嚴(yán)謹(jǐn)留搔,然后該快捷方式是否固定到設(shè)備加了新的判斷標(biāo)準(zhǔn)
二:
由于Android O中引入的后臺執(zhí)行限制,最好使用清單聲明的接收器來接收回調(diào)铛铁。
另外隔显,為了防止其他應(yīng)用程序調(diào)用接收器,需要將屬性賦值android:exported =“false”添加到接收者的清單條目中避归。
分析:針對O系統(tǒng)對于服務(wù)的嚴(yán)格限制荣月,這是對快捷方式功能的擴充
三:
并不是所有的啟動器都支持固定快捷方式!J岜小!所以捐下,如果要確定該APP是否支持國定快捷方式账锹,可以使用isRequestPinShortcutSupported()這個方法的返回值。
根據(jù)返回值坷襟,可以決定隱藏App中允許奸柬,用戶固定快捷方式的選項。該方法返回TRUE婴程,則意味著廓奕,桌面支持requestPinShortcut;
但是用戶也可能會更改档叔,更改選項以后桌粉,再次啟動APP返回值可能就會發(fā)生變化。另外衙四,系統(tǒng)版本低于Android O铃肯,那么它支持使用舊的私有意圖com.android.launcher.action.INSTALL_SHORTCUT。
requestPinShortcut這個方法請求創(chuàng)建固定的快捷方式传蹈。默認(rèn)啟動器將收到該請求押逼,并要求用戶批準(zhǔn)。如果用戶批準(zhǔn)惦界,將創(chuàng)建快捷方式挑格,并且將發(fā)送resultIntent。但是沾歪,如果請求被用戶拒絕漂彤,則不會向呼叫者發(fā)送任何響應(yīng)。需要注意的是,只有具有前臺活動或前臺服務(wù)的應(yīng)用程序才能調(diào)用此方法显歧。否則仪或,它將拋出IllegalStateException。
分析:也就是說士骤,這種啟動器的固定快捷方式能否成功很大程度跟用戶的操作密切相關(guān)(這個可以跟產(chǎn)品吹一波了)范删。另外可以看到O系統(tǒng)對于服務(wù)的嚴(yán)格控制,嚴(yán)格控制后臺服務(wù)需要使用前臺服務(wù)的舉措在于讓應(yīng)用更加安全(因此IntentService在新版本上要慎用拷肌。當(dāng)然谷歌在開發(fā)者文檔上針對后臺服務(wù)這一問題也給了對應(yīng)的解決辦法)到旦,這也看到了谷歌技術(shù)團隊對后續(xù)版本使用應(yīng)用安全的決心。
綜上:可以有以下代碼
@RequiresApi(api = Build.VERSION_CODES.O)
public static void testShortCut(Context context) {
ShortcutManager shortcutManager = (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
boolean requestPinShortcutSupported = shortcutManager.isRequestPinShortcutSupported();
Log.i(TAG, "啟動器是否支持固定快捷方式: "+requestPinShortcutSupported);
if (requestPinShortcutSupported) {
Intent shortcutInfoIntent = new Intent(context, TargetActivity.class);
shortcutInfoIntent.setAction(Intent.ACTION_VIEW);
ShortcutInfo info = new ShortcutInfo.Builder(context, "tzw")
.setIcon(Icon.createWithResource(context, R.drawable.yuanbao))
.setShortLabel("O系統(tǒng)短")
.setLongLabel("O系統(tǒng)長")
.setIntent(shortcutInfoIntent)
.build();
//當(dāng)添加快捷方式的確認(rèn)彈框彈出來時巨缘,將被回調(diào)CallBackReceiver里面的onReceive方法
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, CallBackReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
shortcutManager.requestPinShortcut(info, shortcutCallbackIntent.getIntentSender());
}
}
class CallBackReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive: 固定快捷方式的回調(diào)");
}
}
下面是應(yīng)用啟動后添忘,Android Studio自帶8.0模擬器,首先創(chuàng)建快捷方式的邏輯順序:
點擊ADD...以后若锁,我們的應(yīng)用就顯示這樣了搁骑,可以看到快捷方式向衛(wèi)星一樣掛靠在應(yīng)用icon上面:
如果點擊CANCEL,那么就沒有創(chuàng)建又固,退出以后icon還是原樣仲器,當(dāng)我們第二次打開應(yīng)用,選擇申請桌面快捷方式的時候仰冠,又會彈出上面的申請界面乏冀。
這篇博客花了一些時間,因為要考慮三種不同情況(如果想要看到三種情況的效果洋只,需要手動更改 minSdkVersion版本號辆沦、以及對應(yīng)版本的模擬器資源,或者根據(jù)系統(tǒng)版本號進行自行判斷)识虚,如果寫的不好或者有任何問題可以直接在評論區(qū)或者github指出肢扯。
如果這篇文章對您有開發(fā)or學(xué)習(xí)上的些許幫助,希望各位看官留下寶貴的star舷礼,謝謝鹃彻。
Ps:著作權(quán)歸作者所有,轉(zhuǎn)載請注明作者, 商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處(開頭或結(jié)尾請?zhí)砑愚D(zhuǎn)載出處妻献,添加原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果蛛株。