How to change an application icon programmatically in Android?
Asked Answered
E

12

180

Is it possible to change an application icon directly from the program?
I mean, change icon.png in the res\drawable folder.
I would like to let users to change application's icon from the program so next time they would see the previously selected icon in the launcher.

Espino answered 9/7, 2009 at 10:14 Comment(0)
C
84

It's an old question, but still active as there is no explicit Android feature. And the guys from facebook found a work around - somehow. Today, I found a way that works for me. Not perfect (see remarks at the end of this answer) but it works!

Main idea is, that I update the icon of my app's shortcut, created by the launcher on my home screen. When I want to change something on the shortcut-icon, I remove it first and recreate it with a new bitmap.

Here is the code. It has a button increment. When pressed, the shortcut is replaced with one that has a new counting number.

First you need these two permissions in your manifest:

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />

Then you need this two methods for installing and uninstalling shortcuts. The shortcutAdd method creates a bitmap with a number in it. This is just to demonstrate that it actually changes. You probably want to change that part with something, you want in your app.

private void shortcutAdd(String name, int number) {
    // Intent to be send, when shortcut is pressed by user ("launched")
    Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
    shortcutIntent.setAction(Constants.ACTION_PLAY);

    // Create bitmap with number in it -> very default. You probably want to give it a more stylish look
    Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    Paint paint = new Paint();
    paint.setColor(0xFF808080); // gray
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(50);
    new Canvas(bitmap).drawText(""+number, 50, 50, paint);
    ((ImageView) findViewById(R.id.icon)).setImageBitmap(bitmap);

    // Decorate the shortcut
    Intent addIntent = new Intent();
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);

    // Inform launcher to create shortcut
    addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
    getApplicationContext().sendBroadcast(addIntent);
}

private void shortcutDel(String name) {
    // Intent to be send, when shortcut is pressed by user ("launched")
    Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
    shortcutIntent.setAction(Constants.ACTION_PLAY);

    // Decorate the shortcut
    Intent delIntent = new Intent();
    delIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
    delIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);

    // Inform launcher to remove shortcut
    delIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
    getApplicationContext().sendBroadcast(delIntent);
}

And finally, here are two listener to add the first shortcut and update the shortcut with an incrementing counter.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.test);
    findViewById(R.id.add).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            shortcutAdd("changeIt!", count);
        }
    });
    findViewById(R.id.increment).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            shortcutDel("changeIt!");
            count++;
            shortcutAdd("changeIt!", count);
        }
    });
}

Remarks:

  • This way works also if your App controls more shortcuts on the home screen, e.g. with different extra's in the Intent. They just need different names so that the right one is uninstalled and reinstalled.

  • The programmatical handling of shortcuts in Android is a well known, widely used but not officially supported Android feature. It seems to work on the default launcher and I never tried it anywhere else. So dont blame me, when you get this user-emails "It does not work on my XYZ, double rooted, super blasted phone"

  • The launcher writes a Toast when a shortcut was installad and one when a shortcut was uninstalled. So I get two Toasts every time I change the icon. This is not perfect, but well, as long as the rest of my app is perfect...

Cherianne answered 25/10, 2013 at 15:14 Comment(8)
Today's Calendar app canges icons everyday without a toast.Ressieressler
@ Jim (i think) this is actually a widget thenLargent
@Cherianne What about changing the position of shortcuts after Delete? The shortcut loses its position, which is specified by the user. And placed in any empty cell.Carbrey
shortcutDel unfortunately does not work anymore in Marshmallow, see also https://mcmap.net/q/130920/-trying-to-uninstall_shortcut-but-shortcut-won-39-t-go-awayTwayblade
Not working... #17147228Wilhoit
this replaces the shortcut icon, not launcher iconWoodpecker
In below lines what is Play.class and Constants.ACTION_PLAY Intent shortcutIntent = new Intent(getApplicationContext(), Play.class); shortcutIntent.setAction(Constants.ACTION_PLAY);Northbound
does it wok in android 9?Anaya
P
156

Try this, it works fine for me:

1 . Modify your MainActivity section in AndroidManifest.xml, delete from it, line with MAIN category in intent-filter section

<activity android:name="ru.quickmessage.pa.MainActivity"
    android:configChanges="keyboardHidden|orientation"
    android:screenOrientation="portrait"
    android:label="@string/app_name"
    android:theme="@style/CustomTheme"
    android:launchMode="singleTask">
    <intent-filter>
        ==> <action android:name="android.intent.action.MAIN" /> <== Delete this line
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

2 . Create <activity-alias>, for each of your icons. Like this

<activity-alias android:label="@string/app_name" 
    android:icon="@drawable/icon" 
    android:name=".MainActivity-Red"
    android:enabled="false"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>   
</activity-alias>

3 . Set programmatically: set ENABLE attribute for the appropriate activity-alias

 getPackageManager().setComponentEnabledSetting(
        new ComponentName("ru.quickmessage.pa", "ru.quickmessage.pa.MainActivity-Red"), 
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

Note, At least one must be enabled at all times.

Paulsen answered 6/3, 2013 at 14:11 Comment(21)
sadly works differently across devices I've tried on. Works on HTC Desire 2.2, but unreliable on Galaxy Nexus 4.2.2 and Nexus 7 4.3. On the Galaxy Nexus, can lead to all icons for the app disappearing, and also any widgets being removed. Pity, so I've had to remove this functionality on later devices.Carbide
i made two activity-alias, and pressing a button i disable one component and enable the other, but the icon remains the same (samsung galaxy tab2 android 4.1.1)Wheeze
i cant launch my app since i deleted this : <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />Babylon
This works perfectly for me for switching between launcher icons in the app tray and homescreen, but I've found that the icon used at the OS level (such as for multi-tasking, the uninstall popup, or in the Application Manager listing) remains the original or becomes the default generic Android icon if you haven't set one at the application level in the manifest. Have you found a solution to this, or do you just tolerate a mismatch (or absence) in the icon at the OS level?Wherewith
@jokeefe.. Could you find an answer to your problem above related to icons used at the OS level? I followed the same solution and saw the same behaviour. The icon in the application manager did not update. Could you find a solution for this?Puppy
Error running app. Default activity not found.Photophilous
Error running app. Default activity not foundAuthority
@Jack you should enable at least a default activity-alias.Matty
@KamilIbadov you should enable at least a default activity-alias as well.Matty
@KamilIbadov you just need to add one more alias which reflect your default configuration and set it enable.Matty
@LennonPetrick im getting alias name does not exist in my package any idea ?Headword
@Tej It's not finding your alias name. Did you set the targetActivity correct? And the name without package? You should only put .AliasNameMatty
@Tej Yes. Take a look my code as example: <activity android:name=".splash.SplashScreenActivity" android:screenOrientation="portrait" android:theme="@style/NewAppTheme"/>Matty
@Tej and alias <activity-alias android:name=".Default" android:enabled="true" android:targetActivity=".splash.SplashScreenActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity-alias>Matty
@tal952 Thank you so much. Following the project works perfectly.Kierkegaardian
This mostly works, but after changing the icon, the app disappears from the recents list and I can't figure out why... Any ideas?Formality
In addition to @tal952's link, there is also code at github.com/guardianproject/InformaApp/commit/… in a more complex app. According to the developer, "it has worked pretty universally so far."Erund
This works well. However, the 'DONT_KILL_APP' flag doesn't seem to be working (or I'm understanding it incorrectly). Within a second or two of the icons/activity-aliases being switched, the app is killed and removed from the backstack. The icons are replaced in the launcher, but the app gets killed :/ Any one have ideas on how to prevent this app kill?Eskisehir
This does not seem to work on Android 10, the app gets killed. For older versions of the OS it works fine.Drainpipe
Don't use this on production. Behavior varies across Android versions, and none of them does it right, apparently. Either the app is killed (android 10) or the shortcuts are removed, or the change takes a while to be propagated (android 8)Paint
above code working fine. at the time of live device connect and debug but when I generate a release .apk and then install it, then app is closing without any logs. any one have ideas on how to prevent this app kill?Axinomancy
C
84

It's an old question, but still active as there is no explicit Android feature. And the guys from facebook found a work around - somehow. Today, I found a way that works for me. Not perfect (see remarks at the end of this answer) but it works!

Main idea is, that I update the icon of my app's shortcut, created by the launcher on my home screen. When I want to change something on the shortcut-icon, I remove it first and recreate it with a new bitmap.

Here is the code. It has a button increment. When pressed, the shortcut is replaced with one that has a new counting number.

First you need these two permissions in your manifest:

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />

Then you need this two methods for installing and uninstalling shortcuts. The shortcutAdd method creates a bitmap with a number in it. This is just to demonstrate that it actually changes. You probably want to change that part with something, you want in your app.

private void shortcutAdd(String name, int number) {
    // Intent to be send, when shortcut is pressed by user ("launched")
    Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
    shortcutIntent.setAction(Constants.ACTION_PLAY);

    // Create bitmap with number in it -> very default. You probably want to give it a more stylish look
    Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    Paint paint = new Paint();
    paint.setColor(0xFF808080); // gray
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(50);
    new Canvas(bitmap).drawText(""+number, 50, 50, paint);
    ((ImageView) findViewById(R.id.icon)).setImageBitmap(bitmap);

    // Decorate the shortcut
    Intent addIntent = new Intent();
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
    addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);

    // Inform launcher to create shortcut
    addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
    getApplicationContext().sendBroadcast(addIntent);
}

private void shortcutDel(String name) {
    // Intent to be send, when shortcut is pressed by user ("launched")
    Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
    shortcutIntent.setAction(Constants.ACTION_PLAY);

    // Decorate the shortcut
    Intent delIntent = new Intent();
    delIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
    delIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);

    // Inform launcher to remove shortcut
    delIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
    getApplicationContext().sendBroadcast(delIntent);
}

And finally, here are two listener to add the first shortcut and update the shortcut with an incrementing counter.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.test);
    findViewById(R.id.add).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            shortcutAdd("changeIt!", count);
        }
    });
    findViewById(R.id.increment).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            shortcutDel("changeIt!");
            count++;
            shortcutAdd("changeIt!", count);
        }
    });
}

Remarks:

  • This way works also if your App controls more shortcuts on the home screen, e.g. with different extra's in the Intent. They just need different names so that the right one is uninstalled and reinstalled.

  • The programmatical handling of shortcuts in Android is a well known, widely used but not officially supported Android feature. It seems to work on the default launcher and I never tried it anywhere else. So dont blame me, when you get this user-emails "It does not work on my XYZ, double rooted, super blasted phone"

  • The launcher writes a Toast when a shortcut was installad and one when a shortcut was uninstalled. So I get two Toasts every time I change the icon. This is not perfect, but well, as long as the rest of my app is perfect...

Cherianne answered 25/10, 2013 at 15:14 Comment(8)
Today's Calendar app canges icons everyday without a toast.Ressieressler
@ Jim (i think) this is actually a widget thenLargent
@Cherianne What about changing the position of shortcuts after Delete? The shortcut loses its position, which is specified by the user. And placed in any empty cell.Carbrey
shortcutDel unfortunately does not work anymore in Marshmallow, see also https://mcmap.net/q/130920/-trying-to-uninstall_shortcut-but-shortcut-won-39-t-go-awayTwayblade
Not working... #17147228Wilhoit
this replaces the shortcut icon, not launcher iconWoodpecker
In below lines what is Play.class and Constants.ACTION_PLAY Intent shortcutIntent = new Intent(getApplicationContext(), Play.class); shortcutIntent.setAction(Constants.ACTION_PLAY);Northbound
does it wok in android 9?Anaya
P
35

You cannot change the manifest or the resource in the signed-and-sealed APK, except through a software upgrade.

Paedogenesis answered 9/7, 2009 at 12:50 Comment(30)
I know this is an oldy, doubting between opening a new question and asking here, but here goes: is this still the case? The default 'mail' program (don't know if it is a 'sense' thing) on 2.2.1 changes the icon (or at least, ads an overlay for the number of unread messages)Rood
@Nanne: That's an app widget or home screen feature, not an application icon. You still cannot change the manifest or a resource except via a software upgrade.Paedogenesis
It is what I get when I add the app 'mail' to the homescreen, so if it is a widget, It has a method to add itself when I choose the app. But it might just be a sense thing.Rood
@Nanne: Just because Sense advertises it as a widget does not mean it is a widget.Paedogenesis
? No, I mean the other way around: its not (advertised as) a widget. I add it as an app shortcut. But, as you say, just because this nonstock stuff implies its just an icon, that doesn't mean it is :)Rood
The simpsons game did a in-game update (without asking user permissions) and changed its icon. I think it is storing the icon in the external storage.Expressive
@FranciscoR: You are welcome to provide any actual proof of your claim. The icon in the home screen launcher is defined in the manifest, and that cannot refer to files in external storage. App updates shipped via the Play Store or other distribution outlets can change the launcher icon, but that is not what the OP asked about.Paedogenesis
I'm sorry but I can't. The simpsons app, from time to time shows "uploading data" on the splash screen. One of those times, icon changed from a running Homer to Homer with a giant donut. It wasn't a Play Store automatic update, I have that option disabled.Expressive
The tricky used by P-A appears to work. See any problems in that approach CommonsWare? (besides having a limited number of aliases)Docilla
@NeTeInStEiN: It won't work for all home screens (e.g., those that don't pay attention to component-enabled changes).Paedogenesis
@CommonsWare: Re "that is not what the OP asked about"... Actually it does seem to be what the OP was asking about, although he didn't know there was a distinction between the application icon and the launcher icon. But he said "I would like to let users to change application's icon from the program so next time they would see the previously selected icon in the launcher."Erund
@LarsH: However, the OP was not asking about "App updates shipped via the Play Store or other distribution outlets", which is what "but that is not what the OP asked about" is in reference to. The OP asked to "change an application icon directly from the program".Paedogenesis
@CommonsWare: I see. I misunderstood the referent of 'that'.Erund
@CommonsWare.. So, you suggest not to use the solution given by P-A as it may not work for some phones? Is there a better solution available as of today that can be used or it is not possible at all? Also when I use the solution given by P-A, my icon in Application Manager (in phone settings) is not updated. can you suggest if that can be resolved using this solution?Puppy
@Sushil: "So, you suggest not to use the solution given by P-A as it may not work for some phones?" -- I have been told that this does not work with all home screen implementations. Phones are irrelevant. "Is there a better solution available as of today" -- no. There is no requirement for a home screen launcher to show icons at all, let alone the icon that you want. There is no requirement that home screens pay attention to changes in enabled settings, though they should.Paedogenesis
@CommonsWare.. thanks for your comment. And do you have an idea how can I update the icons in application manager and widgets using P-A or some other solution?Puppy
@Sushil: I do not know what "application manager" refers to. You control the UI of your own app widgets. If you have further concerns in this area, ask a fresh Stack Overflow question.Paedogenesis
@CommonsWare.. I am referring to the application manager in the phone settings from where we get application details. Also from there, you can force stop the application or uninstall it or clear cache/data etc.Puppy
@Sushil: That would be the android:icon on <application>. The only way to change that is by replacing the APK with one with a higher versionCode that contains your desired icon.Paedogenesis
Not true anymore. Google Calendar on Android 6+ changes in the launcher on a daily basis. Today, the icon is a "2", yesterday it was an "1". Usually, there was a "31" on the icon. But not anymore, it changes. Anyone knows how this is possible?Corespondent
@Bobby: Does it do that on every home screen app in the Play Store?Paedogenesis
What exactly do you mean?Corespondent
@Bobby: I mean that there are hundreds, if not thousands, of home screen implementations on the Play Store, in addition to the hundreds of different pre-installed home screens across the thousands of Android device models that exist. Those home screen implementations are welcome to have hooks that allow for dynamic launcher icon replacement. However, not all home screens have to offer this. Just because you are seeing this behavior for one app on one home screen on one device does not mean that it is available for all apps on all home screens on all devices.Paedogenesis
Well, you're absolutely right it seems. It changes on Nova & Nova prime, but not on OnePlus-Stock or GoogleNow-Launcher. Thanks, learned something new! :)Corespondent
this information should be out there. Had I not clicked the "show more comments", I never had seen this. I've been looking for this concrete answer all day!Woodpecker
how to change like calender appGritty
" I mean that there are hundreds, if not thousands, of home screen implementations on the Play Store, in addition to the hundreds of different pre-installed home screens across the thousands of Android device models that exist...Just because you are seeing this behavior for one app on one home screen on one device does not mean that it is available for all apps on all home screens on all devices." @Paedogenesis is this statement still valid? Because I see Calender changes the Icon everyday. I am using google pixel.Clyde
@Sniper: "is this statement still valid?" -- yes. "Because I see Calender changes the Icon everyday. I am using google pixel" -- Google wrote Android, the Google Calendar app, and the Pixel Launcher. Google can create their own hooks to allow Google to do what Google wants.Paedogenesis
There are other Non-Google Apps that change Icon during the festive season. Swiggy, changed their icon today for the Christmas. Although I am not so sure if they did it by an update or the solutions like - create an Alias of the LAUNCHER Activity and change the Component setting accordingly.Clyde
@Sniper: "Swiggy, changed their icon today for the Christmas" -- the typical solution for this, as you suggest, is via an update to the app.Paedogenesis
W
17

Programatically, you may want to publish the application launcher yourself :

Note: this method no longer works starting with Android 8.0 - Oreo

In your AndroidManifest.xml, add :

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>

Then you need create your app launcher intent:

Intent myLauncherIntent = new Intent();
myLauncherIntent.setClassName("your.package.name", "YourLauncherActivityName");
myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Create an install shortcut intent with your app launcher and custom icon:

Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, myLauncherIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Application Name");
intent.putExtra
       (
        Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
        Intent.ShortcutIconResource.fromContext
                                    (
                                         getApplicationContext(), 
                                         R.drawable.app_icon
                                    )
       );
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");

And finally launch the broadcast intent:

getApplicationContext().sendBroadcast(intent);
Weatherworn answered 22/3, 2011 at 21:49 Comment(1)
this replaces the shortcut icon, not the launcher iconWoodpecker
R
9

@P-A's solution partially works for me. Detail my findings below:

1) The first code snippet is incorrect, see below:

<activity
    ...
    <intent-filter>
        ==> <action android:name="android.intent.action.MAIN" /> <== This line shouldn't be deleted, otherwise will have compile error
        <category android:name="android.intent.category.LAUNCHER" /> //DELETE THIS LINE
    </intent-filter>
</activity>

2) Should use following code to disable all icons before enabling another one, otherwise it will add a new icon, instead of replacing it.

getPackageManager().setComponentEnabledSetting(
        getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

BUT, if you use code above, then shortcut on homescreen will be removed! And it won't be automatically added back. You might be able to programmatically add icon back, but it probably won't stay in the same position as before.

3) Note that the icon won't get changed immediately, it might take several seconds. If you click it right after changing, you might get an error saying: "App isn't installed".

So, IMHO this solution is only suitable for changing icon in app launcher only, not for shortcuts (i.e. the icon on homescreen)

Reexamine answered 1/7, 2016 at 5:43 Comment(2)
Error running app. Default activity not found.Photophilous
you delete the launcher, how will it find the default activity @Reexamine ?Cicily
S
8

As mentioned before you need use <activity-alias> to change the application icon. To avoid killing the application after enabling appropriate activity-alias you need to do this after the application is killed. To find out if the application was killed you can use this method

  1. Create activity aliases in AndroidManifest.xml
<activity android:name=".ui.MainActivity"/>

<activity-alias
    android:name=".one"
    android:icon="@mipmap/ic_launcher_one"
    android:targetActivity=".ui.MainActivity"
    android:enabled="true">

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

</activity-alias>

<activity-alias
    android:name=".two"
    android:icon="@mipmap/ic_launcher_two"
    android:targetActivity=".ui.MainActivity"
    android:enabled="false">

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

</activity-alias>
  1. Сreate a service that will change the active activity-alias after killing the application. You need store the name of new active activity-alias somewhere (e.g. SharedPreferences)
class ChangeAppIconService: Service() {
    private val aliases = arrayOf(".one", ".two")

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onTaskRemoved(rootIntent: Intent?) {
        changeAppIcon()
        stopSelf()
    }

    fun changeAppIcon() {
        val sp = getSharedPreferences("appSettings", Context.MODE_PRIVATE)

        sp.getString("activeActivityAlias", ".one").let { aliasName ->
            if (!isAliasEnabled(aliasName)) {
                setAliasEnabled(aliasName)
            }
        }
    }

    private fun isAliasEnabled(aliasName: String): Boolean {
        return packageManager.getComponentEnabledSetting(
            ComponentName(
                this,
                "${BuildConfig.APPLICATION_ID}$aliasName"
            )
        ) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    }

    private fun setAliasEnabled(aliasName: String) {
        aliases.forEach {
            val action = if (it == aliasName)
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED
            else
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                
            packageManager.setComponentEnabledSetting(
                ComponentName(
                    this,
                    "${BuildConfig.APPLICATION_ID}$aliasName"
                ),
                action,
                PackageManager.DONT_KILL_APP
            )
        }
    }
}
  1. Add service to AndroidManifest.xml
<service 
    android:name=".ChangeAppIconService"
    android:stopWithTask="false"
    />
  1. Start ChangeAppIconService in MainActivity.onCreate
class MainActivity: Activity {

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
       ...

       startService(Intent(this, ChangeAppIconService::class.java))

       ...
    }

    ...

}
Synsepalous answered 3/2, 2021 at 14:25 Comment(3)
Good solution, Dmitry, Thanks. But one more moment - you should add this method to service: override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d("ChangeAppIconService", "Service Started") return START_NOT_STICKY } This will handle the situation, when the user swipes the app process in trayElectrometer
onTaskRemoved() is only called when app is removed from Recent Task List by user...Crank
Xiaomi MIUI doesn't call onTaskRemoved() :(Crank
P
7

Assuming you mean changing the icon shown on the home screen, this could easily be done by creating a widget that does exactly this. Here's an article that demonstrate how that can be accomplished for a "new messages" type application similar to iPhone:

http://www.cnet.com/8301-19736_1-10278814-251.html

Pinnatifid answered 9/7, 2009 at 10:25 Comment(1)
URL is no longer valid. Please try to provide a complete answer here without relying on outside URLs since those can very easily go offline.Selinaselinda
O
6

Try this solution

<activity android:name=".SplashActivity"
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity-alias android:label="ShortCut"
        android:icon="@drawable/ic_short_cut"
        android:name=".SplashActivityAlias"
        android:enabled="false"
        android:targetActivity=".SplashActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

Add the following code when you want to change your app icon

PackageManager pm = getPackageManager();
                    pm.setComponentEnabledSetting(
                            new ComponentName(YourActivity.this,
                                    "your_package_name.SplashActivity"),
                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                            PackageManager.DONT_KILL_APP);

                    pm.setComponentEnabledSetting(
                            new ComponentName(YourActivity.this,
                                    "your_package_name.SplashActivityAlias"),
                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                            PackageManager.DONT_KILL_APP);
Oleic answered 5/10, 2018 at 9:14 Comment(2)
above code working fine. at the time of live device connect and debug but when I generate a release .apk and then install it, then app is closing without any logs. any one have ideas on how to prevent this app kill?Axinomancy
Same question any way to prevent this app kill?Rorke
N
4

AndroidManifest.xml example:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name="com.pritesh.resourceidentifierexample.MainActivity"
                  android:label="@string/app_name"
                  android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <!--<category android:name="android.intent.category.LAUNCHER"/>-->
            </intent-filter>
        </activity>

        <activity-alias android:label="RED"
                        android:icon="@drawable/ic_android_red"
                        android:name="com.pritesh.resourceidentifierexample.MainActivity-Red"
                        android:enabled="true"
                        android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias android:label="GREEN"
                        android:icon="@drawable/ic_android_green"
                        android:name="com.pritesh.resourceidentifierexample.MainActivity-Green"
                        android:enabled="false"
                        android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

        <activity-alias android:label="BLUE"
                        android:icon="@drawable/ic_android_blue"
                        android:name="com.pritesh.resourceidentifierexample.MainActivity-Blue"
                        android:enabled="false"
                        android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

    </application>

Then follow below given code in MainActivity:

ImageView imageView = (ImageView)findViewById(R.id.imageView);
            int imageResourceId;
            String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
            int hours = new Time(System.currentTimeMillis()).getHours();
            Log.d("DATE", "onCreate: "  + hours);

            getPackageManager().setComponentEnabledSetting(
                    getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

            if(hours == 13)
            {
                imageResourceId = this.getResources().getIdentifier("ic_android_red", "drawable", this.getPackageName());
                getPackageManager().setComponentEnabledSetting(
                        new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Red"),
                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
            }else if(hours == 14)
            {
                imageResourceId = this.getResources().getIdentifier("ic_android_green", "drawable", this.getPackageName());
                getPackageManager().setComponentEnabledSetting(
                        new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Green"),
                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

            }else
            {
                imageResourceId = this.getResources().getIdentifier("ic_android_blue", "drawable", this.getPackageName());
                getPackageManager().setComponentEnabledSetting(
                        new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Blue"),
                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

            }

            imageView.setImageResource(imageResourceId);
Newman answered 9/8, 2017 at 18:53 Comment(2)
I'm getting com.pritesh.resourceidentifierexample.MainActivity-Red doesn't exist in com.pritesh.resourceidentifierexample exception . here I've used your manifest name just to demonstrate my problemHeadword
@PriteshPatel above code working fine. at the time of live device connect and debug but when I generate a release .apk and then install it, then app is closing without any logs. Do you know why?Axinomancy
P
3

Applying the suggestions mentioned, I've faced the issue of app getting killed whenever default icon gets changed to new icon. So have implemented the code with some tweaks. Step 1). In file AndroidManifest.xml, create for default activity with android:enabled="true" & other alias with android:enabled="false". Your will not contain but append those in with android:enabled="true".

       <activity
        android:name=".activities.SplashActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/SplashTheme">

    </activity>
    <!-- <activity-alias used to change app icon dynamically>   : default icon, set enabled true    -->
    <activity-alias
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:name=".SplashActivityAlias1" <!--put any random name started with dot-->
        android:enabled="true"
        android:targetActivity=".activities.SplashActivity"> <!--target activity class path will be same for all alias-->
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
    <!-- <activity-alias used to change app icon dynamically>  : sale icon, set enabled false initially -->
    <activity-alias
        android:label="@string/app_name"
        android:icon="@drawable/ic_store_marker"
        android:roundIcon="@drawable/ic_store_marker"
        android:name=".SplashActivityAlias" <!--put any random name started with dot-->
        android:enabled="false"
        android:targetActivity=".activities.SplashActivity"> <!--target activity class path will be same for all alias-->
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

Step 2). Make a method that will be used to disable 1st activity-alias that contains default icon & enable 2nd alias that contains icon need to be changed.

/**
 * method to change the app icon dynamically
 *
 * @param context
 * @param isNewIcon  : true if new icon need to be set; false to set default 
 * icon
 */

public static void changeAppIconDynamically(Context context, boolean isNewIcon) {
    PackageManager pm = context.getApplicationContext().getPackageManager();
    if (isNewIcon) {
        pm.setComponentEnabledSetting(
                new ComponentName(context,
                        "com.example.dummy.SplashActivityAlias1"), //com.example.dummy will be your package
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);

        pm.setComponentEnabledSetting(
                new ComponentName(context,
                        "com.example.dummy.SplashActivityAlias"),
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    } else {
        pm.setComponentEnabledSetting(
                new ComponentName(context,
                        "com.example.dummy.SplashActivityAlias1"),
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);

        pm.setComponentEnabledSetting(
                new ComponentName(context,
                        "com.example.dummy.SplashActivityAlias"),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }
}

Step 3). Now call this method depending on your requirement, say on button click or date specific or occasion specific conditions, simply like -

// Switch app icon to new icon
    GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, true);
// Switch app icon to default icon
            GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, false);

Hope this will help those who face the issue of app getting killed on icon change. Happy Coding :)

Perennial answered 12/5, 2020 at 16:17 Comment(2)
above code working fine. at the time of live device connect and debug but when I generate a release .apk and then install it, then app is closing without any logs. Do you know why?Axinomancy
any clue for the current answers @Abhijeet?Giddings
N
0

To get the solution by Markus working I needed the first Intent so be:

Intent myLauncherIntent = new Intent(Intent.ACTION_MAIN);
            myLauncherIntent.setClassName(this,  this.getClass().getName());
            myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Niello answered 5/5, 2013 at 14:42 Comment(0)
F
0

PackageManager.DONT_KILL_APP ignored since Android 10, which means system would exit our app back to launcher, provided no apis to work around, but I found an approach with cost: mark your startup activity as android:launchMode="singleInstance"

<activity
    android:name=".startpage.StartPageActivity"
    android:exported="true"
    android:screenOrientatsavedInstanceStateion="portrait"
    android:launchMode="singleInstance"
    android:icon="@mipmap/ic_launcher"
    android:theme="@style/StartPageTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity
    android:name=".startpage.StartPageActivity2"
    android:exported="true"
    android:screenOrientation="portrait"
    android:enabled="false"
    android:icon="@mipmap/ic_launcher_earning"
    android:theme="@style/StartPageThemeEarning">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity-alias
    android:name=".startpage.StartPageActivity3"
    android:enabled="false"
    android:icon="@mipmap/ic_launcher_earning"
    android:theme="@style/StartPageThemeEarning"
    android:targetActivity=".startpage.StartPageActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

<style name="StartPageTheme" parent="AppTheme">
    <item name="android:windowBackground">@drawable/def_launch</item>
</style>

<style name="StartPageThemeEarning" parent="AppTheme">
    <item name="android:windowBackground">@drawable/def_launch_earning</item>
</style>

I wish to change app logo and window background, but only logo apply, the declared theme apply nothing to StartPageActivity3

do setComponentEnabledSetting after StartPageActivity e.g MainActivity's first resume:

private fun swapReal(activity: BaseActivity, isClosingApp: Boolean) {
    val packageManager = activity.applicationContext.packageManager
    if (isClosingApp || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        packageManager.setComponentEnabledSetting(
            ComponentName(activity, StartPageActivity::class.java),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
        )
        packageManager.setComponentEnabledSetting(
            ComponentName(activity, StartPageActivity2::class.java),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
        )

        prefInstance.remove(PREF_KEY_CHANGEBG)
    } else {
        packageManager.setComponentEnabledSetting(
            ComponentName(
                activity,
                ".StartPageActivity"
            ),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
        )
        packageManager.setComponentEnabledSetting(
            ComponentName(
                activity,
                ".StartPageActivity3"
            ),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
        )

        prefInstance.putBoolean(PREF_KEY_CHANGEBG, true)
    }
}

fun changeWindowBackground(activity: StartPageActivity) {
    val shouldChangeBg = MMKV.defaultMMKV().getBoolean(PREF_KEY_CHANGEBG, false)
    if (shouldChangeBg) {
        activity.window.setBackgroundDrawable(R.drawable.def_launch_earning)
    }
}

class StartPageActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        changeWindowBackground(this)
        setContentView(...)
    }
}

well, how's damage of mark launchMode as singleInstance, when you press home key or swipe from bottom to launcher, later back to app by tap app's icon, you would see the StartPageActivity show again.

this problem cannot be solve, so my solution only suit those app which has only one Activity in most time such as game app.

Freedman answered 12/1, 2024 at 5:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.