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)