Come modificare un'icona dell'applicazione a livello di codice in Android?


153

È possibile cambiare l'icona di un'applicazione direttamente dal programma?
Voglio dire, cambia icon.pngnella res\drawablecartella.
Vorrei consentire agli utenti di modificare l'icona dell'applicazione dal programma in modo che la prossima volta vedessero l'icona precedentemente selezionata nel programma di avvio.

Risposte:


80

È una vecchia domanda, ma ancora attiva in quanto non esiste una funzione Android esplicita. E i ragazzi di Facebook hanno trovato un lavoro - in qualche modo. Oggi ho trovato un modo che funziona per me. Non perfetto (vedere le osservazioni alla fine di questa risposta) ma funziona!

L'idea principale è che aggiorno l'icona del collegamento della mia app, creata dal programma di avvio nella mia schermata principale. Quando voglio cambiare qualcosa sull'icona del collegamento, la rimuovo per prima e la ricrea con una nuova bitmap.

Ecco il codice Ha un bottone increment. Se premuto, il collegamento viene sostituito con uno che ha un nuovo numero di conteggio.

Per prima cosa hai bisogno di queste due autorizzazioni nel tuo manifest:

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

Quindi hai bisogno di questi due metodi per installare e disinstallare le scorciatoie. Il shortcutAddmetodo crea una bitmap con un numero al suo interno. Questo è solo per dimostrare che in realtà cambia. Probabilmente vuoi cambiare quella parte con qualcosa che vuoi nella tua 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);
}

E infine, ecco due ascoltatori per aggiungere il primo collegamento e aggiornare il collegamento con un contatore incrementale.

@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);
        }
    });
}

Osservazioni:

  • In questo modo funziona anche se la tua app controlla più scorciatoie nella schermata principale, ad es. Con diversi extra in Intent. Hanno solo bisogno di nomi diversi in modo che quello giusto sia disinstallato e reinstallato.

  • La gestione programmatica delle scorciatoie in Android è una funzionalità Android ben nota, ampiamente utilizzata ma non ufficialmente supportata. Sembra funzionare sul launcher predefinito e non l'ho mai provato da nessun'altra parte. Quindi non dare la colpa a me quando ricevi queste e-mail dell'utente "Non funziona sul mio XYZ, telefono con doppio root, super distrutto"

  • Il programma di avvio scrive Toastquando è stato installato un collegamento e uno quando è stato disinstallato un collegamento. Quindi ricevo due Toasts ogni volta che cambio l'icona. Questo non è perfetto, ma bene, purché il resto della mia app sia perfetto ...


9
L'app Calendario di oggi crea icone ogni giorno senza brindisi.
Jim McKeeth,

1
@ Jim (penso) questo è in realtà un widget allora
JacksOnF1re

3
scorciatoiaDel purtroppo non funziona più in Marshmallow, vedi anche stackoverflow.com/a/33731620/1545993
Taifun

2
questo sostituisce l'icona di collegamento, non l'icona di avvio
user1506104

3
Nelle righe seguenti cos'è Play.class e Constants.ACTION_PLAY Scorciatoia intento = nuovo intento (getApplicationContext (), Play.class); shortcutIntent.setAction (Constants.ACTION_PLAY);
Dasharath Singh Bajroliya,

136

Prova questo, funziona bene per me:

1 Modifica la tua MainActivitysezione in AndroidManifest.xml, elimina da essa, allinea con la MAINcategoria nella intent-filtersezione

<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 Crea <activity-alias>, per ciascuna delle tue icone. Come questo

<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 Imposta a livello di programmazione: imposta l'attributo ENABLE per l'appropriatoactivity-alias

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

Nota, almeno uno deve essere abilitato in ogni momento.


3
purtroppo funziona diversamente su tutti i dispositivi che ho provato. Funziona su HTC Desire 2.2, ma inaffidabile su Galaxy Nexus 4.2.2 e Nexus 7 4.3. Sul Galaxy Nexus, può portare alla scomparsa di tutte le icone per l'app e alla rimozione di qualsiasi widget. Peccato, quindi ho dovuto rimuovere questa funzionalità su dispositivi successivi.
Richard Le Mesurier,

7
non riesco ad avviare la mia app da quando ho eliminato questo: <action android: name = "android.intent.action.MAIN" /> <categoria android: name = "android.intent.category.LAUNCHER" />
noobProgrammer

1
Questo funziona perfettamente per me per il passaggio tra le icone di avvio nella barra delle applicazioni e nella schermata iniziale, ma ho scoperto che l'icona utilizzata a livello di sistema operativo (come per il multi-tasking, il popup di disinstallazione o nell'elenco di Application Manager) rimane l'originale o diventa l'icona Android generica predefinita se non ne hai impostato uno a livello di applicazione nel manifest. Hai trovato una soluzione a questo o tolleri semplicemente una mancata corrispondenza (o assenza) nell'icona a livello di sistema operativo?
scherzo il

2
Errore durante l'esecuzione dell'app. Attività predefinita non trovata.
CopsOnRoad,

1
Errore durante l'esecuzione dell'app. Attività predefinita non trovata
Kamil Ibadov,

36

Non è possibile modificare il manifest o la risorsa nell'APK firmato e sigillato, se non tramite un aggiornamento del software.


2
@Nanne: questo è un widget app o una funzione della schermata principale, non un'icona dell'applicazione. Non è ancora possibile modificare il manifest o una risorsa se non tramite un aggiornamento del software.
CommonsWare il

1
? No, intendo il contrario: non è (pubblicizzato come) un widget. Lo aggiungo come scorciatoia per l'app. Ma, come dici tu, solo perché questa roba nonstock implica che è solo un'icona, ciò non significa che sia :)
Nanne

2
@NeTeInStEiN: non funzionerà per tutte le schermate Home (ad esempio, quelle che non prestano attenzione alle modifiche abilitate ai componenti).
Commons War

1
Non è più vero Google Calendar su Android 6+ cambia nel programma di avvio su base giornaliera. Oggi l'icona è un "2", ieri era un "1". Di solito, c'era un "31" sull'icona. Ma non più, cambia. Qualcuno sa come sia possibile?
UeliDeSchwert,

1
@Bobby: voglio dire che ci sono centinaia, se non migliaia, di implementazioni della schermata iniziale sul Play Store, oltre alle centinaia di diverse schermate home preinstallate tra le migliaia di modelli di dispositivi Android esistenti. Le implementazioni della schermata iniziale sono benvenute per avere hook che consentono la sostituzione dinamica dell'icona di avvio. Tuttavia, non tutte le schermate Home devono offrire questo. Solo perché stai vedendo questo comportamento per un'app su una schermata principale su un dispositivo non significa che sia disponibile per tutte le app su tutte le schermate Home su tutti i dispositivi.
Commons War

17

A livello di programmazione, potresti voler pubblicare tu stesso il programma di avvio dell'applicazione:

Nota: questo metodo non funziona più a partire da Android 8.0 - Oreo

Nel tuo AndroidManifest.xml, aggiungi:

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

Quindi è necessario creare l'intento di avvio dell'app:

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

Crea un intento di scelta rapida per l'installazione con il programma di avvio dell'app e l'icona personalizzata:

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");

E infine lanciare l'intento di trasmissione:

getApplicationContext().sendBroadcast(intent);

questo sostituisce l'icona di collegamento, non l'icona di avvio
user1506104


9

La soluzione di @ PA funziona parzialmente per me. Descrivi di seguito i miei risultati:

1) Il primo frammento di codice non è corretto, vedi sotto:

<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) Dovresti usare il seguente codice per disabilitare tutte le icone prima di abilitarne un'altra, altrimenti aggiungerà una nuova icona, invece di sostituirla.

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

MA, se si utilizza il codice sopra, il collegamento sulla schermata iniziale verrà rimosso! E non verrà automaticamente aggiunto di nuovo. Potresti essere in grado di aggiungere nuovamente a livello di codice l'icona, ma probabilmente non rimarrà nella stessa posizione di prima.

3) Nota che l'icona non verrà cambiata immediatamente, potrebbero essere necessari alcuni secondi. Se fai clic subito dopo la modifica, potresti ricevere un errore che dice: "L'app non è installata".

Quindi, IMHO questa soluzione è adatta solo per cambiare icona solo nel programma di avvio delle app, non per le scorciatoie (ovvero l'icona nella schermata iniziale)


2
Errore durante l'esecuzione dell'app. Attività predefinita non trovata.
CopsOnRoad,

elimini il programma di avvio, come troverà l'attività predefinita @Deqing?
j2emanue,

5

Prova questa soluzione

<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>

Aggiungi il seguente codice quando vuoi cambiare l'icona della tua app

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);

4

AndroidManifest.xml esempio:

<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>

Quindi seguire il codice indicato di seguito 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);

Ricevo com.pritesh.resourceidentifierexample.MainActivity-Red doesn't exist in com.pritesh.resourceidentifierexampleun'eccezione. qui ho usato il tuo nome manifest solo per dimostrare il mio problema
Tejas Pandya,

0

Per far funzionare la soluzione di Markus avevo bisogno del primo Intento, quindi sii:

Intent myLauncherIntent = new Intent(Intent.ACTION_MAIN);
            myLauncherIntent.setClassName(this,  this.getClass().getName());
            myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

0

Applicando i suggerimenti citati, ho affrontato il problema dell'app che viene uccisa ogni volta che l'icona predefinita viene cambiata in nuova icona. Quindi ho implementato il codice con alcune modifiche. Passo 1). Nel file AndroidManifest.xml, crea l'attività predefinita con android: enabled = "true" e altri alias con android: enabled = "false". Il tuo non conterrà ma aggiungi quelli con 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>

Passo 2). Creare un metodo che verrà utilizzato per disabilitare il 1 ° alias attività che contiene l'icona predefinita e abilitare il 2 ° alias che contiene icona deve essere modificato.

/**
 * 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);
    }
}

Passaggio 3). Ora chiama questo metodo in base alle tue esigenze, di 'clic sul pulsante o data specifica o condizioni specifiche occasione, semplicemente come

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

Spero che questo possa aiutare coloro che affrontano il problema dell'app che viene uccisa al cambio di icona. Happy Coding :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.