DEV Community

Ankur Anand
Ankur Anand

Posted on

Let Users Change Your App Icon: A Guide to Dynamic Icons on Android with Activity-Alias

Have you ever wanted to let your users choose different app icons? Maybe:

  • A premium subscriber icon 🥇
  • A holiday-themed icon 🎄
  • Or just different color themes 🌈

You might think this requires hacky workarounds or third-party tools — but it doesn’t.

Android supports this natively using the powerful (and often overlooked) <activity-alias> tag.

In this guide, I’ll walk you through creating a dynamic app icon switcher that’s:

  • Native
  • Reliable
  • Easy to implement

✨ The Magic Ingredient: activity-alias

An activity-alias is essentially a shortcut to your actual activity. Each alias can have:

  • Its own icon
  • A custom label
  • A separate enabled/disabled state

Only one alias is active at a time — and that’s the icon users see on their home screen.


🛠️ Step 1: Configure AndroidManifest.xml

You’ll need:

  1. Your main launcher activity (without an intent filter)
  2. Multiple <activity-alias> entries pointing to it — one for each icon
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:icon="@mipmap/default_icon"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/default_icon_round"
        android:theme="@style/Theme.YourTheme"
        tools:targetApi="31">

        <!-- Main Activity (no LAUNCHER intent filter) -->
        <activity
            android:name="com.example.webview.MainActivity"
            android:exported="true" />

        <!-- Default Icon Alias (enabled by default) -->
        <activity-alias
            android:name="com.example.webview.DefaultIconAlias"
            android:targetActivity="com.example.webview.MainActivity"
            android:icon="@mipmap/default_icon"
            android:exported="true"
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <!-- Alternate Icons (initially disabled) -->
        <activity-alias
            android:name="com.example.webview.PixelPopIconAlias"
            android:targetActivity="com.example.webview.MainActivity"
            android:icon="@mipmap/pixel_pop_icon"
            android:exported="true"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name="com.example.webview.BoltifyIconAlias"
            android:targetActivity="com.example.webview.MainActivity"
            android:icon="@mipmap/boltify_icon"
            android:exported="true"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <!-- Optional clone for managing default state -->
        <activity-alias
            android:name="com.example.webview.CloneDefaultIconAlias"
            android:targetActivity="com.example.webview.MainActivity"
            android:icon="@mipmap/default_icon"
            android:exported="true"
            android:enabled="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

    </application>
</manifest>
Enter fullscreen mode Exit fullscreen mode

🔍 Key Points

  • Your actual MainActivity doesn’t handle the LAUNCHER intent.
  • Each activity-alias points to the same MainActivity.
  • Only one alias is enabled="true" at a time — that becomes the visible app icon.
  • Other aliases are set to enabled="false" until switched.
  • This allows dynamic icon changes without restarting the app.

🧠 Step 2: Icon Switching Logic in Java

Use the PackageManager to enable/disable alias components dynamically.

private void changeApkIcon(String componentName) {
    String apkComponentName = prefs.getString(APK_ICON_SELECTED_COMPONENT_NAME, defaultIconAlias);

    Log.d("IconSwitch", "Changing icon: " + apkComponentName + " → " + componentName);

    if (apkComponentName.equals(componentName)) return;

    if (componentName.equals(defaultIconAlias)) {
        setComponentEnabled(apkComponentName, getApplicationContext(), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
        setComponentEnabled(cloneDefaultIconAlias, getApplicationContext(), PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
        prefs.edit().putString(APK_ICON_SELECTED_COMPONENT_NAME, cloneDefaultIconAlias).apply();
    } else {
        int disableState = apkComponentName.contains(defaultIconAlias)
                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;

        setComponentEnabled(apkComponentName, getApplicationContext(), disableState);
        setComponentEnabled(componentName, getApplicationContext(), PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
        prefs.edit().putString(APK_ICON_SELECTED_COMPONENT_NAME, componentName).apply();
    }
}

private void setComponentEnabled(String componentName, Context context, int state) {
    PackageManager manager = context.getPackageManager();
    manager.setComponentEnabledSetting(
        new ComponentName(context, componentName),
        state,
        PackageManager.DONT_KILL_APP
    );
}
Enter fullscreen mode Exit fullscreen mode

🔍 What this does:

  1. Retrieves the current active icon from SharedPreferences.
  2. Skips any changes if the selected icon is already active.
  3. Switches back to default icon by:
    • Disabling the current icon alias (setting it to COMPONENT_ENABLED_STATE_DEFAULT)
    • Enabling the CloneDefaultIconAlias
  4. Switches to a non-default icon by:
    • Disabling the current alias (with correct disable mode)
    • Enabling the newly selected alias
  5. Stores the selected alias in SharedPreferences for persistence across launches.

🎛️ Step 3: Trigger the Change via UI

Hook this logic to a button, dropdown, or other selector in your UI.

loadButton.setOnClickListener(v -> {
    String newIconName = iconNameInput.getSelectedItem().toString();
    int index = iconNameInput.getSelectedItemPosition();

    if (!newIconName.isBlank()) {
        changeApkIcon(apkIconComponentNameEnabledList.get(index));
    }
});
Enter fullscreen mode Exit fullscreen mode

⚠️ Important Considerations

⏱️ Icon Refresh Delay

  • The icon change is not always instant.
  • Some launchers (especially OEM/custom ones) cache the app icon for a few seconds or more.
  • In most cases, the new icon appears within a few seconds.

🧪 Emulator Quirks

  • If you reinstall the app and the default enabled alias has changed, the emulator might fail to install it.
  • However, if you're using a release build and simply upgrade the app, everything works fine.
  • This issue is limited to emulator behavior and doesn’t affect real devices.

✅ Google Play Policy

This method is fully compliant with Google Play policies, provided:

  • You're not using icon switching to hide the app or perform deceptive behavior.
  • You’re transparent with users about icon changes (e.g., offering them as part of customization or premium features).
  • You stick to the standard Android API without abusing permissions.

🚀 Bonus Ideas

Here are some creative ways to use dynamic app icons:

  • 🎨 Theme customization: Let users change icons as part of a light/dark or custom theme.
  • 🏆 Achievement rewards: Unlock special icons after completing milestones.
  • 📅 Seasonal icons: Update icons for holidays (e.g., 🎃 Halloween, 🎄 Christmas).
  • 💎 Premium exclusives: Give subscribers access to gold or exclusive branding icons.
  • 🧩 A/B testing: Try different branding variations for select users (with caution!).

🔚 Wrapping Up

Dynamic icon switching can significantly boost user engagement and make your app feel more personal and fun.

With activity-alias, you can implement this:

  • ✅ Natively (no hacks or custom launchers)
  • ✅ Cleanly (single manifest config)
  • ✅ Safely (Play Store compliant)

📎 Sample project on GitHub!

GitHub Repo Link

Download the latest apk from releases and can test it yourself.

Thanks for reading — happy coding!

Top comments (0)