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)