search
LoginSignup
16
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

Androidのアプリアイコンとタイトルを動的に変更する

Android には activity-alias というアクティビティに別名をつける仕組みがあります。この機能をうまく使うことでアプリのアイコンやタイトルを動的に変更してみたので、そのメモ書きです。これは iOS 10.3 から追加された User-Selectable App Icons に近いことを実現できそうです。

他にも以下の機能でランチャーにアプリのショートカットを作成するということでアプリアイコンとタイトルを変更したように見せる方法もありますが、この記事では触れません。

以降のコードや実行結果は下記の環境のものとなります。

  • Android Studio : 3.3.2
  • Kotlin : 1.3.21
  • Target SDK : 28

アプリアイコンとタイトルを変更する手順

マニフェストファイルにメインアクティビティのエイリアスを作成する

AndroidManifest.xml に次のように記述を変更します。

  • メインアクティビティから <intent-filter> 削除
  • デフォルトで利用するアプリアイコンとタイトルを指定した <activity-alias> を追加
    • android:iconandroid:label がそれぞれアプリアイコンとタイトル
    • android:name はエイリアス名なので、実在しないクラスでOK
    • android:targetActivity が実体となるアクティビティ
    • android:enabled はデフォルト使用するものなので true としておく
    • <intent-filter> はこのエイリアスの方で指定する
  • デフォルトの代替として利用するアプリアイコンとタイトルを指定した <activity-alias> を追加
    • android:iconandroid:label に代替となるアプリアイコンとタイトルを指定する
    • android:enabledfalse としておく

このように、指定するアイコンやタイトルは静的なものですので、ダウンロードしたアイコンを指定するというようなことはおそらくできないと思われます。(この点も、iOS の User-Selectable App Icon と似ていますね)

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.appiconchanger">

    <application
            android:allowBackup="true"
            android:label="@string/app_name"
            android:icon="@mipmap/ic_launcher"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">

        <!-- メインアクティビティ -->
        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar">
            <!-- intent-filter は記述しません -->
        </activity>

        <!-- メインアクティビティのエイリアス1 (デフォルトアイコン/タイトル) -->
        <activity-alias android:label="@string/app_name"
                        android:icon="@mipmap/ic_launcher"
                        android:name=".MainActivity_default"
                        android:enabled="true"
                        android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <!-- メインアクティビティのエイリアス2 (代替アイコン/タイトル) -->
        <activity-alias android:label="@string/app_name_beta"
                        android:icon="@mipmap/ic_launcher_beta"
                        android:name=".MainActivity_beta"
                        android:enabled="false"
                        android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

    </application>

</manifest>

記述に不整合があると Android Studio でのビルド時に Manifest の Merge エラーが出ます。その際は、AndroidManifest.xml の Merged Manifest タブを表示することでエラー内容を確認することができますので、それを頼りにエラーを解消しましょう。(下図はメインアクティビティ本体の方に <intent-filter> が記述されていた時のエラー)

スクリーンショット 2019-03-12 13.57.26.png

有効なエイリアスを切り替えるコードを書く

下記のコードは、MainActivity に配置した FAB を押すたびにデフォルトエイリアスと代替エイリアスを切り替える例です。

MainActivity.kt
package com.example.appiconchanger

import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity;

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        // ボタンを押すたびにエイリアスを切り替える
        fab.setOnClickListener { view ->
            // デフォルト ComponentName
            val defaultComponentName = ComponentName(packageName, packageName + ".MainActivity_default")

            // 代替 ComponentName
            val substituteComponentName = ComponentName(packageName, packageName + ".MainActivity_substitute")

            // エイリアスを切り替える
            val state = packageManager.getComponentEnabledSetting(substituteComponentName)
            if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
                packageManager.setComponentEnabledSetting(
                    substituteComponentName,
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                    PackageManager.DONT_KILL_APP)
                packageManager.setComponentEnabledSetting(defaultComponentName,
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP)
                Snackbar.make(view, "Switch to substitute alias.", Snackbar.LENGTH_LONG).show()

            } else {
                packageManager.setComponentEnabledSetting(
                    defaultComponentName,
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                    PackageManager.DONT_KILL_APP)
                packageManager.setComponentEnabledSetting(
                    substituteComponentName,
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP)
                Snackbar.make(view, "Switch to default alias.", Snackbar.LENGTH_LONG).show()
            }
        }
    }

}

検証

実行結果

下図のようにアプリのアイコンとタイトルを変更することに成功しました。左画像がデフォルトエイリアス、右画像が代替エイリアスです。

default.png substitute.png

いくつかのデバイスで試してみた

ランチャーにアプリのショートカットアイコンを作成する方法の場合、デバイスや Android のバージョンによって動作しないケースがあったため、今回のエイリアスによる方法を手元にあるデバイスで試してみた結果が下記の表です。

デバイス Android ver. 結果 備考
Google Nexus 5X 8.1
Galaxy S6 edge (SCV31) 7.0
Google Nexus 5 6.0.1 変更後にランチャーがクラッシュ?して再起動された。変更はされている。
Google Nexus 5 5.1.1

Google Nexus 5 (6.0.1) のみランチャーがクラッシュしてしまう問題がありましたので、他のデバイスでも何らかの問題が起きる可能性がありそうです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
16
Help us understand the problem. What are the problem?