Android動態更換應用圖標

語言: CN / TW / HK

一、背景

近日,微博官方發佈了一項新功能,即可以在App設置中動態更換微博的顯示圖標樣式。根據微博官方的説法,除了最原始的圖標外,微博還推出了另外10種不同的樣式,既有3D微博、炫彩微博等保留了眼睛造型的新樣式,也有奶酪甜馨、巧克力等以食物命名的“新口味”,還有夢幻紫、幻想星空等抽象派新造型,給了微博用户多種選擇的自由。

不過需要注意的是,這一功能並不是面對所有人開放的,只有微博年費會員才能享受。此外,iOS 10.3及以上和Android 10及以上系統版本支持該功能,但是iPad與一加8Pro手機無法使用該功能。因部分手機存在系統差異,會導致該功能不可用,微博方面後續還會對該功能進行進一步優化。

image.png

二、技術實現

其實,説到底,上述功能用到的是動態更換桌面圖標的技術。如果説多年以前,實現圖標的切換還是一種時髦的技術,那麼,我們可以直接使用PackageManager就可以實現動態更換桌面圖標。

實現的細節是,在Manifest文件中使用標籤準備多個Activity入口,沒個activity都指向入口Activity,並且為每個擁有標籤的activity設置單獨的icon和應用名,最後調用SystemService 服務kill掉launcher,並執行launcher的重啟操作。

首先,我們在AndroidManifest.xml文件中添加如下代碼:

```

<!-- 權限-->
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/wb_default_logo"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/wb_default_logo"
    android:supportsRtl="true"
    android:theme="@style/Theme.AndroidDemo">

   ...//省略其他代碼

    <!-- 默認微博-->
    <activity-alias
        android:name="com.xzh.demo.default"
        android:targetActivity=".MainActivity"
        android:label="@string/app_name"
        android:enabled="false"
        android:icon="@mipmap/wb_default_logo"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

    <!-- 3D微博-->
    <activity-alias
        android:name=".threedweibo"
        android:targetActivity=".MainActivity"
        android:label="@string/wb_3d"
        android:enabled="false"
        android:icon="@mipmap/wb_3dweibo"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

    ... //省略其他

</application>

``` 上面配置中涉及到的屬性如下: - android:name:註冊的組件名字,啟動組件的名稱。 - android:enabled:是否啟用這個組件,也就是是否顯示這個入口。 - android:icon:圖標 - android:label:名稱 - android:targetActivity:默認的activity沒有這個屬性,指定目標activity,與默認的activity中的name屬性是一樣的,需要有相應的java類文件。

接着,我們在MainActivity觸發Logo圖標更換邏輯,代碼如下:

``` class MainActivity : AppCompatActivity() { var list: List = ArrayList() var recyclerView: RecyclerView? = null var adapter: LogoAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView() initData() initRecycle() } private fun initView() { recyclerView = findViewById(R.id.recycle_view) } private fun initData() { list = Arrays.asList( LogoBean(R.mipmap.wb_default_logo, "默認圖標", true), LogoBean(R.mipmap.wb_3dweibo, "3D微博", false), LogoBean(R.mipmap.wb_cheese_sweetheart, "奶酪甜心", false), LogoBean(R.mipmap.wb_chocolate_sweetheart, "巧克力", false), LogoBean(R.mipmap.wb_clear_colorful, "清透七彩", false), LogoBean(R.mipmap.wb_colorful_sunset, "多彩日落", false), LogoBean(R.mipmap.wb_colorful_weibo, "炫彩微博", false), LogoBean(R.mipmap.wb_cool_pool, "清涼泳池", false), LogoBean(R.mipmap.wb_fantasy_purple, "夢幻紫", false), LogoBean(R.mipmap.wb_fantasy_starry_sky, "幻想星空", false), LogoBean(R.mipmap.wb_hot_weibo, "熱感微博", false), ) } private fun initRecycle() { adapter =LogoAdapter(this,list); val layoutManager = GridLayoutManager(this, 3) recyclerView?.layoutManager = layoutManager recyclerView?.adapter = adapter adapter?.setOnItemClickListener(object : OnItemClickListener { override fun onItemClick(view: View?, position: Int) { if(position==1){ changeLogo("com.xzh.demo.threedweibo") }else if (position==2){ changeLogo("com.xzh.demo.cheese") }else if (position==3){ changeLogo("com.xzh.demo.chocolate") }else { changeLogo("com.xzh.demo.default") } } }) }

fun changeLogo(name: String) {
    val pm = packageManager
    pm.setComponentEnabledSetting(
        componentName,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
    )
    pm.setComponentEnabledSetting(
        ComponentName(this, name),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
    )
    reStartApp(pm)
}
fun reStartApp(pm: PackageManager) {
    val am = getSystemService(ACTIVITY_SERVICE) as ActivityManager
    val intent = Intent(Intent.ACTION_MAIN)
    intent.addCategory(Intent.CATEGORY_HOME)
    intent.addCategory(Intent.CATEGORY_DEFAULT)
    val resolveInfos = pm.queryIntentActivities(intent, 0)
    for (resolveInfo in resolveInfos) {
        if (resolveInfo.activityInfo != null) {
            am.killBackgroundProcesses(resolveInfo.activityInfo.packageName)
        }
    }
}

} `` 注意上面的changeLogo()方法中的字符串需要和AndroidManifest.xml文件中的`的name相對應。運行上面的代碼,然後點擊應用中的某個圖標,就可以更換應用的桌面圖標,如下圖所示。

image.png 不過,測試的時候也遇到一些適配問題:

  • 小米9:版本升級時,新版本在AndroidManifest中刪除A3,老版本切換圖標到A3,為卸載直接覆蓋安裝新版本,手機桌面圖標消失。
  • magic 4:版本升級時,新版本在AndroidManifest中刪除A3,老版本切換圖標到A3,為卸載直接覆蓋安裝新版本,手機桌面圖標切換到默認圖標,但點擊之後未能打開APP。