Skip to content

SamriddhaS/DynamicAppLauncherDemo

Repository files navigation

Dynamic App Launcher Icon Demo

This project demonstrates how to implement dynamic app launcher icons in Android applications using Jetpack Compose. Popular apps like Swiggy, Zomato, and Twitter use this feature to dynamically change their app icons, enhancing user engagement and branding.


Update : 12/12/24

Changed the implementation a little to make sure not to kill the app with every icon change. The app will still get killed once when changing from the default icon. Thank you Avinav Srivastava for suggesting this change.

---

📌 Features

  • Switch between multiple app launcher icons dynamically.
  • Uses ActivityAlias to manage app icons.
  • Includes a Jetpack Compose-based UI to select and apply app icons seamlessly.

🎥 Preview


📋 Table of Contents

  1. Setup and Requirements
  2. Implementation Highlights
  3. Usage
  4. Blog Links Reated to this topic
  5. Contributing

⚙️ Setup and Requirements

Requirements

  • Android Studio Arctic Fox or later.
  • Minimum SDK version: 21.
  • Recommended target SDK version: 31 or higher.

Setup

  1. Clone the repository:
    git clone https://github.com/SamriddhaS/DynamicAppLauncherDemo.git
  2. Open the project in Android Studio.
  3. Build and run the project on a physical device or emulator.

✨ Implementation Highlights

1. Manifest Configuration

We define ActivityAlias entries in the AndroidManifest.xml to manage multiple app icons. Each alias is mapped to the main activity but uses a different icon. We can add activity alias for each of the launcher icon that we want to configure.

<!-- This is enabled by default -->
<activity-alias
    android:name=".DefaultTheme"
    android:enabled="true"
    android:icon="@mipmap/ic_launcher"
    android:targetActivity=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

<!-- Icon one: disabled by default -->
<activity-alias
    android:name=".MyIconOne"
    android:enabled="false"
    android:icon="@mipmap/my_launch_icon_one"
    android:targetActivity=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

<!-- Icon two: disabled by default -->
<activity-alias
    android:name=".MyIconTwo"
    android:enabled="false"
    android:icon="@mipmap/my_launch_icon_two"
    android:targetActivity=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

2. Dynamic Icon Switching

The MainActivity uses the PackageManager API to enable or disable ActivityAlias entries dynamically.

packageManager.setComponentEnabledSetting(
    ComponentName(
        this,
        "$packageName${selectedIcon.themeName}"
    ),
    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    PackageManager.DONT_KILL_APP
)

packageManager.setComponentEnabledSetting(
    ComponentName(
        this,
        "$packageName${currentIcon}"
    ),
    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP
)

3. Shared Preferences

We store the currently active icon in SharedPreferences to persist user preferences.

sharedPref.edit {
    putString(CURRENT_ICON_KEY, selectedIcon.themeName)
    apply()
}

4. Jetpack Compose UI

A Compose-based UI allows users to select and preview the app icons dynamically.

@Composable
fun ImageSelector(
    onImageClick: (AppLauncherIcons) -> Unit,
    modifier: Modifier
) {
    val imageList = listOf(
        AppLauncherIcons.Default_Theme,
        AppLauncherIcons.Theme_One,
        AppLauncherIcons.Theme_Two,
        AppLauncherIcons.Theme_Three,
    )
    
    LazyColumn(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        items(imageList.size) { index ->
            val imageRes = imageList[index]
            GlideImage(
                model = imageRes.imageRes,
                contentDescription = "Image $index",
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .size(100.dp)
                    .clickable { onImageClick(imageRes) }
                    .padding(4.dp)
            )
        }
    }
}


🚀 Usage

  1. Launch the app.
  2. Browse through the available launcher icons using the provided UI.
  3. Select an icon to apply it as the app launcher icon.
  4. Confirm the selection in the modal bottom sheet.

Here are some helpful blogs that inspired and guided the implementation of this feature:


🤝 Contributing

Contributions are welcome! Please fork the repository, make your changes, and submit a pull request.


Enjoy experimenting with dynamic app icons! 🚀

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages