アプリのアイコンを希望に合わせて変更できる

#### Jetpack Composeでアプリのアイコンを希望に合わせて変更できる
Android アプリのアイコンをプログラムで変更する。
最近、𝕏 Blue ユーザーがアプリのアイコンを変更できることが発表されました。 Reddit アプリでも同様の機能が提供されているため、この機能は新しいものではありません。
実装は特に難しいものではないので、ここで共有したいと思います。

AndroidManifest.xml で activity-alias 要素を宣言することで、複数のランチャー アイコンを作成できます。 アプリのアイコンを変更すると、現在のアイコンを無効にして新しいアイコンを有効にすることで効果が得られます。

#### 設定：
<ins>*こちらで```MainActivity```一つActivity使用してます</ins>
#### ```AndroidManifest.xml```に```activity-alias```要素を宣言する

#### <ins>Step 1: </ins>
<ins> ```android:launchMode="singleTop"``` にする</ins>

```AndroidManifest.xml
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@style/Theme.AndroidBlog">
</activity>
```

#### <ins>Step 2: </ins>
<ins>複数のランチャー アイコンを作成</ins>

<img width="600" alt="Screen Recording 2023-11-23 at 21.45.48.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3619500/86e8fd2a-e722-127e-6442-450331b7bc96.gif">

#### <ins>Step 3: </ins>
<ins>```values/strings.xml```に```<string name="labelName">App Label Name</string> ```します</ins>

```strings.xml
    <string name="app_name_a">Android A</string>
    <string name="app_name_b">Android B</string>
    <string name="app_name_c">Android C</string>
    <string name="app_name_d">Android D</string>
    <string name="app_name_e">Android E</string>
    <string name="app_name_f">Android F</string>
    <string name="app_name_g">Android G</string>
    <string name="app_name_h">Android H</string>
```

#### <ins>Step 4: </ins>
<ins>```activity-alias```要素を宣言する</ins>
[https://gist.github.com/ihridoydas/20c02eea3f711b42298e8234226161ed](https://gist.github.com/ihridoydas/20c02eea3f711b42298e8234226161ed)

```AndroidManifest.xml
 <!--End For App Icon Changer-->
        <activity-alias
            android:name=".MainActivity"
            android:enabled="true"
            android:exported="true"
            android:label="@string/app_name"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <!--Start For App Icon Changer-->
        <activity-alias
            android:name=".MainActivityA"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_a"
            android:label="@string/app_name_a"
            android:roundIcon="@mipmap/ic_launcher_a_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityB"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_b"
            android:label="@string/app_name_b"
            android:roundIcon="@mipmap/ic_launcher_b_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityC"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_c"
            android:label="@string/app_name_c"
            android:roundIcon="@mipmap/ic_launcher_c_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityD"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_d"
            android:label="@string/app_name_d"
            android:roundIcon="@mipmap/ic_launcher_d_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityE"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_e"
            android:label="@string/app_name_e"
            android:roundIcon="@mipmap/ic_launcher_e_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityF"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_f"
            android:label="@string/app_name_f"
            android:roundIcon="@mipmap/ic_launcher_f_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityG"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_g"
            android:label="@string/app_name_g"
            android:roundIcon="@mipmap/ic_launcher_g_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityH"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_h"
            android:label="@string/app_name_h"
            android:roundIcon="@mipmap/ic_launcher_h_round"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>
        <activity-alias
            android:name=".StartActivity"
            android:exported="true"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>
        <!--End For App Icon Changer-->

```

#### <ins>Step 5: </ins>
<ins>```AppIcon.kt``` dataクラスを作成</ins>
[https://gist.github.com/ihridoydas/c19b70c004ec774ddb2e65f361e34a61](https://gist.github.com/ihridoydas/c19b70c004ec774ddb2e65f361e34a61)

```AppIcon.kt
data class AppIcon(
    val component: String,

    @DrawableRes
    val foregroundResource: Int,
)
```
#### <ins>Step 6: </ins>
<ins>```AndroidAppIcons.kt``` AppIconのListを作成</ins>
[https://gist.github.com/ihridoydas/ba0a4b63bb0978b347feeff3c15f1594](https://gist.github.com/ihridoydas/ba0a4b63bb0978b347feeff3c15f1594)

```AndroidAppIcons.kt
val androidAppIcons: List<AppIcon> = listOf(
    AppIcon(
        component = "com.hridoy.androidblog.MainActivity",
        foregroundResource = R.drawable.ic_launcher_foreground,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityA",
        foregroundResource = R.drawable.a,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityB",
        foregroundResource = R.drawable.b,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityC",
        foregroundResource = R.drawable.c,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityD",
        foregroundResource = R.drawable.d,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityE",
        foregroundResource = R.drawable.e,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityF",
        foregroundResource = R.drawable.f,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityG",
        foregroundResource = R.drawable.g,
    ),
    AppIcon(
        component = "com.hridoy.androidblog.MainActivityH",
        foregroundResource = R.drawable.h,
    ),

)
```

#### <ins>Step 7: </ins>
<ins>Enabling a Component ```setIcon：```</ins>AppIconを```packageManager```Enabledする

AndroidManifest.xml ではクラス名だけを指定できますが、コンポーネント名を指定する場合は、パッケージ名を含む完全修飾クラス名を指定する必要があります。 COMPONENT_ENABLED_STATE_ENABLED 状態で setComponentEnabledSetting() を実行すると、ランチャーに新しいアプリ アイコンが表示されます。

```setIcon.kt
private fun setIcon(context: Context, componentName: String) {
    val packageManager = context.packageManager

    androidAppIcons.filter {
        it.component != componentName
    }.forEach {
        packageManager.setComponentEnabledSetting(
            ComponentName(context, it.component),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP,
        )
    }

    packageManager.setComponentEnabledSetting(
        ComponentName(context, componentName),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP,
    )
}
```
#### <ins>Step 8: </ins>
<ins>Compose View を作成する</ins>
[https://gist.github.com/ihridoydas/25b8bceee5fa93c5977149f648fc8980](https://gist.github.com/ihridoydas/25b8bceee5fa93c5977149f648fc8980)

```AppIconChangerScreen.kt
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AppIconChangerScreen(
    modifier: Modifier = Modifier,
    icons: List<AppIcon>,
) {
    Column(modifier = modifier.fillMaxSize()) {
        Spacer(modifier = Modifier.size(48.dp))

        Text(
            modifier = Modifier.fillMaxWidth(),
            text = "App Icon Change What you want!!",
            color = Color.LightGray,
            style = MaterialTheme.typography.headlineMedium,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
        )

        Spacer(modifier = Modifier.size(48.dp))

        FlowRow(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp),
            horizontalArrangement = Arrangement.SpaceEvenly,
        ) {
            icons.forEach {
                AppIconOption(
                    modifier = Modifier
                        .padding(16.dp)
                        .size(54.dp),
                    appIcon = it,
                )
            }
        }
    }
}

@Composable
private fun AppIconOption(
    modifier: Modifier = Modifier,
    appIcon: AppIcon,
) {
    val context = LocalContext.current

    Image(
        modifier = modifier
            .drawBehind {
                drawCircle(color = Color(0xFF536972))
            }
            .clip(CircleShape)
            .clickable(
                enabled = true,
                onClick = {
                    setIcon(
                        context = context,
                        componentName = appIcon.component,
                    )
                },
            )
            .padding(all = 4.dp),
        painter = painterResource(id = appIcon.foregroundResource),
        contentDescription = null,
    )
}

@Preview(
    showBackground = true,
    showSystemUi = true,
)
@Composable
fun GreetingPreview() {
    AndroidBlogTheme {
        AppIconChangerScreen(
            modifier = Modifier
                .fillMaxWidth()
                .background(Color(0xFF161D26)),
            icons = androidAppIcons,
        )
    }
}
```
#### 結果: </ins>
<ins>[Github サンプル](https://github.com/ihridoydas/AndroidBlog/tree/blog1/AppIconChange)</ins>

![AppChanger.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3619500/520428b5-95c4-c779-26d0-4f30fc03df95.gif)