Android動態更換應用圖示

xiangzhihong發表於2022-07-08

一、背景

近日,微博官方釋出了一項新功能,即可以在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檔案中新增如下程式碼:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xzh.demo">
    
    <!-- 許可權-->
    <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>
</manifest>

上面配置中涉及到的屬性如下:

  • android:name:註冊的元件名字,啟動元件的名稱。
  • android:enabled:是否啟用這個元件,也就是是否顯示這個入口。
  • android:icon:圖示
  • android:label:名稱
  • android:targetActivity:預設的activity沒有這個屬性,指定目標activity,與預設的activity中的name屬性是一樣的,需要有相應的java類檔案。

接著,我們在MainActivity觸發Logo圖示更換邏輯,程式碼如下:

class MainActivity : AppCompatActivity() {
    var list: List<LogoBean> = 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檔案中的<activity-alias>的name相對應。執行上面的程式碼,然後點選應用中的某個圖示,就可以更換應用的桌面圖示,如下圖所示。

image.png

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

  • 小米9:版本升級時,新版本在AndroidManifest中刪除A3,老版本切換圖示到A3,為解除安裝直接覆蓋安裝新版本,手機桌面圖示消失。
  • magic 4:版本升級時,新版本在AndroidManifest中刪除A3,老版本切換圖示到A3,為解除安裝直接覆蓋安裝新版本,手機桌面圖示切換到預設圖示,但點選之後未能開啟APP。

相關文章