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:
- Your main launcher activity (without an intent filter)
- 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>
๐ Key Points
- Your actual
MainActivity
doesnโt handle theLAUNCHER
intent. - Each
activity-alias
points to the sameMainActivity
. - 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
);
}
๐ What this does:
-
Retrieves the current active icon from
SharedPreferences
. - Skips any changes if the selected icon is already active.
-
Switches back to default icon by:
- Disabling the current icon alias (setting it to
COMPONENT_ENABLED_STATE_DEFAULT
) - Enabling the
CloneDefaultIconAlias
- Disabling the current icon alias (setting it to
-
Switches to a non-default icon by:
- Disabling the current alias (with correct disable mode)
- Enabling the newly selected alias
-
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));
}
});
โ ๏ธ 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)