How to Change app Icon dynamically in Jetpack Compose | Tips & Tricks

Change App Icon Dynamically in Android App

Overview

Have you ever noticed how apps like VLC, Zomato, Blinkit, or Zepto suddenly change their app icons during festive seasons like Diwali, Holi, or Christmas? It’s a small but powerful detail that instantly grabs attention. This kind of visual transformation not only enhances user engagement but also adds a sense of freshness and relevance to the app experience. 

As developers, it naturally sparks our curiosity: “How are they changing the app icon without updating the app?” The ability to dynamically switch your app icon allows you to celebrate festivals, highlight campaigns, or reflect a user’s personal choice - all without pushing an app update. 


In this article, we’ll explore how to implement dynamic icon switching in Android using native tools like activity-alias, giving your app a festive flair - like a Christmas VLC icon - and keeping your users intrigued every time they unlock their phones.

πŸ”Ή What is activity-alias?

activity-alias is a special Android manifest component that acts as an alternate name and icon for an existing activity (usually your launcher activity). You can define multiple aliases pointing to the same activity but with different icons or labels.


πŸ”Ή How It Helps with Icon Switching

By defining multiple activity-alias entries (each with a unique icon), you can enable one and disable the others at runtime using PackageManager. Only the enabled alias appears in the launcher as your app's icon.

  • πŸŽ„ Change the app icon based on festivals or seasons (e.g., Diwali, Christmas).
  • πŸ‘€ Offer personalized icon themes (e.g., Light vs. Dark).
  • πŸ“£ Promote limited-time campaigns without needing a Play Store update.

Key Features Table

Feature Description
πŸŽ„ Festive Icon Switching Change app icon to a Christmas VLC theme using activity-alias.
πŸ”” Scheduled Holiday Updates Use AlarmManager to switch icons on Christmas Day.
πŸ‘€ User-Driven Themes Allow users to toggle between normal and Christmas VLC icons.

Implementation Steps

In this code, we’ve created a simple UI with normal and Christmas VLC icon previews and a switch to toggle between them - just like how Zomato and Zepto change icons for events. 

If you want your app to automatically switch its launcher icon during festivals like Diwali, Holi, or Christmas, you can achieve this cleanly using Android’s AlarmManager. AlarmManager allows you to schedule a task to be executed at a specific date and time, even if your app is not actively running. 

This makes it perfect for triggering an icon change exactly on a festival day - without requiring user interaction.

Step 1: Define activity-alias in AndroidManifest.xml

Create alternate launcher entries for normal and Christmas VLC icons.


<!-- Normal VLC Icon (Enabled by Default) -->
<activity-alias
    android:name=".DefaultVLC"
    android:enabled="true"
    android:exported="true"
    android:icon="@mipmap/ic_vlc_launcher"
    android:roundIcon="@mipmap/ic_vlc_launcher_round"
    android:label="@string/app_name"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

<!-- Christmas VLC Icon (Initially Disabled) -->
<activity-alias
    android:name=".ChristmasVLC"
    android:enabled="false"
    android:exported="true"
    android:icon="@mipmap/ic_vlc_christmas_launcher"
    android:roundIcon="@mipmap/ic_vlc_christmas_launcher_round"
    android:label="@string/app_name"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>
    

Step 2: Set Up Main Activity

Create the main activity to host the Jetpack Compose UI for icon switching.


package com.boltuix.iconchanger

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.boltuix.iconchanger.ui.theme.IconChangerTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            IconChangerTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    HomeScreen()
                }
            }
        }
    }
}
    

Step 3: Create Festive UI with Jetpack Compose

Build a UI with normal and Christmas VLC icon previews and a toggle switch.


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
    val context = LocalContext.current
    var isDefaultSelected by remember { mutableStateOf(true) }

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(
                        "VLC Icon Switcher",
                        style = TextStyle(textAlign = TextAlign.Center),
                        modifier = Modifier.fillMaxWidth()
                    )
                }
            )
        }
    ) { paddingValues ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
        ) {
            // Normal VLC Icon Preview
            Image(
                painter = painterResource(id = R.drawable.vlc_default),
                contentDescription = "Normal VLC Icon",
                modifier = Modifier
                    .align(Alignment.CenterStart)
                    .size(100.dp)
            )

            // Christmas VLC Icon Preview
            Image(
                painter = painterResource(id = R.drawable.vlc_christmas),
                contentDescription = "Christmas VLC Icon",
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .size(100.dp)
            )

            // Switch Toggle
            Column(
                modifier = Modifier.align(Alignment.Center),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = if (isDefaultSelected) "Normal VLC Icon" else "Christmas VLC Icon"
                )
                Switch(
                    checked = isDefaultSelected,
                    onCheckedChange = {
                        isDefaultSelected = it
                        if (isDefaultSelected) {
                            // Enable default, disable special
                            switchIcon("DefaultVLC", "ChristmasVLC", context)
                        } else {
                            // Enable special, disable default
                            switchIcon("ChristmasVLC", "DefaultVLC", context)
                        }
                    }
                )
            }
        }
    }
}
    

Step 4: Switch Icons with PackageManager

Enable the desired activity-alias to update the launcher icon.


private fun switchIcon(enableAlias: String, disableAlias: String, context: Context) {
    val pm = context.packageManager
    val enableComponent = ComponentName(context, "com.boltuix.iconchanger.$enableAlias")
    val disableComponent = ComponentName(context, "com.boltuix.iconchanger.$disableAlias")

    pm.setComponentEnabledSetting(
        enableComponent,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP
    )

    pm.setComponentEnabledSetting(
        disableComponent,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP
    )
}
    

 Step 5: Schedule πŸ”” Christmas Icon Change (Optional)

Use AlarmManager to automatically switch to the Christmas VLC icon on December 25th.

Schedule tasks to trigger icon changes without user interaction, ensuring timely festive updates.


πŸ“‚ Full Source Code

Below is the complete source code for the dynamic icon switching app, combining all steps into a single implementation.

AndroidManifest.xml


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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_vlc_launcher"
        android:roundIcon="@mipmap/ic_vlc_launcher_round"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.IconChanger"
        tools:targetApi="31">

        <!-- Base MainActivity (used by all aliases) -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.IconChanger" />

        <!-- Normal VLC Icon (Enabled by Default) -->
        <activity-alias
            android:name=".DefaultVLC"
            android:enabled="true"
            android:exported="true"
            android:icon="@mipmap/ic_vlc_launcher"
            android:roundIcon="@mipmap/ic_vlc_launcher_round"
            android:label="@string/app_name"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <!-- Christmas VLC Icon (Initially Disabled) -->
        <activity-alias
            android:name=".ChristmasVLC"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_vlc_christmas_launcher"
            android:roundIcon="@mipmap/ic_vlc_christmas_launcher_round"
            android:label="@string/app_name"
            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>
    

MainActivity.kt


package com.boltuix.iconchanger

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.boltuix.iconchanger.ui.theme.IconChangerTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            IconChangerTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    HomeScreen()
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
    val context = LocalContext.current
    var isDefaultSelected by remember { mutableStateOf(true) }

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(
                        "VLC Icon Switcher",
                        style = TextStyle(textAlign = TextAlign.Center),
                        modifier = Modifier.fillMaxWidth()
                    )
                }
            )
        }
    ) { paddingValues ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
        ) {
            // Normal VLC Icon Preview
            Image(
                painter = painterResource(id = R.drawable.vlc_default),
                contentDescription = "Normal VLC Icon",
                modifier = Modifier
                    .align(Alignment.CenterStart)
                    .size(100.dp)
            )

            // Christmas VLC Icon Preview
            Image(
                painter = painterResource(id = R.drawable.vlc_christmas),
                contentDescription = "Christmas VLC Icon",
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .size(100.dp)
            )

            // Switch Toggle
            Column(
                modifier = Modifier.align(Alignment.Center),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = if (isDefaultSelected) "Normal VLC Icon" else "Christmas VLC Icon"
                )
                Switch(
                    checked = isDefaultSelected,
                    onCheckedChange = {
                        isDefaultSelected = it
                        if (isDefaultSelected) {
                            // Enable default, disable special
                            switchIcon("DefaultVLC", "ChristmasVLC", context)
                        } else {
                            // Enable special, disable default
                            switchIcon("ChristmasVLC", "DefaultVLC", context)
                        }
                    }
                )
            }
        }
    }
}

/**
 * This function enables one icon alias and disables the other.
 * The launcher icon gets switched without killing the app.
 *
 * @param enableAlias The component name to enable (e.g., "DefaultVLC")
 * @param disableAlias The component name to disable (e.g., "ChristmasVLC")
 * @param context The application context
 */
private fun switchIcon(enableAlias: String, disableAlias: String, context: Context) {
    val pm = context.packageManager

    val enableComponent = ComponentName(context, "com.boltuix.iconchanger.$enableAlias")
    val disableComponent = ComponentName(context, "com.boltuix.iconchanger.$disableAlias")

    pm.setComponentEnabledSetting(
        enableComponent,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP
    )

    pm.setComponentEnabledSetting(
        disableComponent,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP
    )
}
    

FAQ

πŸŽ„ Which Android versions support dynamic icon switching?

activity-alias is supported on Android 4.4 (API 19) and above, covering nearly all modern devices.


πŸ”” Does AlarmManager require special permissions for scheduling?

No, basic AlarmManager usage for icon switching needs no additional permissions, but ensure exact timing for reliability.


πŸ‘€ Will the icon change reflect instantly for users?

Yes, the change is immediate, but some launchers may require a restart or refresh to display the new icon.


πŸ“£ Can I support multiple festive icons?

Yes, add more activity-alias entries for festivals like Diwali or Holi and toggle them as needed.


Community Feedback

Have you implemented dynamic icon switching for holidays or campaigns? Share your tips, fixes, or alternative approaches in the comments to help the Android community. We’ll update this guide with your feedback!


Join Reddit to get more updates like this.

https://www.reddit.com/r/JetpackComposeDev/comments/1mkoirp/how_to_dynamically_change_app_icons_in_jetpack/

Post a Comment

Previous Post Next Post