Как программно изменить значок приложения в Android?

Можно ли изменить значок приложения непосредственно из программы?
Я имею в виду, измените icon.png в папке res\drawable.
Я хочу, чтобы пользователи меняли значок приложения из программы, чтобы в следующий раз они увидели ранее выбранный значок в панели запуска.

Ответ 1

Это старый вопрос, но он все еще активен, поскольку нет явной функции Android. И ребята из facebook нашли работу вокруг - как-то. Сегодня я нашел способ, который работает для меня. Не идеально (см. Примечания в конце этого ответа), но он работает!

Основная идея заключается в том, что я обновляю значок ярлыка моего приложения, созданного при запуске на моем главном экране. Когда я хочу что-то изменить на ярлыке, я сначала удаляю его и воссоздаю его с помощью нового растрового изображения.

Вот код. Он имеет кнопку increment. При нажатии ярлык заменяется на тот, у которого есть новый счетный номер.

Сначала вам нужны эти два разрешения в вашем манифесте:

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

Затем вам понадобятся два метода установки и удаления ярлыков. Метод shortcutAdd создает растровое изображение с числом в нем. Это просто, чтобы продемонстрировать, что оно действительно меняется. Вероятно, вы захотите изменить эту часть с чем-то, чего хотите в своем приложении.

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

И, наконец, вот два слушателя, чтобы добавить первый ярлык и обновить ярлык с помощью счетчика приращений.

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

Примечания:

  • Этот способ также работает, если ваше приложение контролирует больше ярлыков на главном экране, например. с разным дополнительным значением в Intent. Они просто нуждаются в разных именах, так что правый удаляется и переустанавливается.

  • Программная обработка ярлыков в Android - это хорошо известная, широко используемая, но не официально поддерживаемая функция Android. Кажется, что он работает с пусковой установкой по умолчанию, и я никогда не пробовал ее нигде. Так что не обвиняйте меня, когда вы получаете это электронное письмо пользователя "Это не работает на моем XYZ, двойном корне, супер взорванном телефоне"

  • Программа запуска запускает Toast, когда ярлык был installad и один, когда ярлык был удален. Поэтому каждый раз, когда я меняю значок, я получаю два Toast. Это не идеально, но хорошо, если остальное приложение отлично...

Ответ 2

Попытайтесь, это отлично работает для меня:

1. Измените раздел MainActivity в AndroidManifest.xml, удалите из него строку с MAIN категорией в разделе intent-filter

<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. Создайте <activity-alias> для каждого из ваших значков. Как это

<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. Установите программно: установите атрибут ENABLE для соответствующего activity-alias

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

Примечание. По крайней мере один должен быть включен в любое время.

Ответ 3

Вы не можете изменить манифест или ресурс в APK с подписью и печатью, за исключением обновления программного обеспечения.

Ответ 4

С программной точки зрения вы можете самостоятельно опубликовать панель запуска приложений:

Примечание: этот метод больше не работает, начиная с Android 8.0 - Oreo

В вашем AndroidManifest.xml добавьте:

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

Затем вам нужно создать намерение запуска приложения:

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

Создайте ярлык для установки с помощью панели запуска вашего приложения и пользовательского значка:

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

И, наконец, начать трансляцию намерения:

getApplicationContext().sendBroadcast(intent);

Ответ 5

Предполагая, что вы хотите изменить значок, показанный на главном экране, это легко сделать, создав виджет, который делает именно это. Вот статья, демонстрирующая, как это можно сделать для приложения типа "новые сообщения", похожего на iPhone:

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

Ответ 6

@P-решение частично работает для меня. Подробно мои выводы ниже:

1) Первый фрагмент кода неверен, см. ниже:

<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) Если вы используете следующий код, чтобы отключить все значки до включения другого, в противном случае он добавит новый значок вместо его замены.

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

НО, если вы используете код выше, то ярлык на рабочем столе будет удален! И он не будет автоматически добавлен обратно. Возможно, вы сможете программно добавить значок назад, но он, вероятно, не останется в том же положении, что и раньше.

3) Обратите внимание, что значок не будет изменен сразу, это может занять несколько секунд. Если вы щелкните по нему сразу после изменения, вы можете получить сообщение об ошибке: "Приложение не установлено".

Итак, IMHO это решение подходит только для изменения значка в приложении запуска приложения, а не для ярлыков (например, значок на рабочем столе)

Ответ 7

AndroidManifest.xml пример:

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

Затем следуйте ниже приведенному коду в 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);

Ответ 8

Попробуйте это решение

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

Добавьте следующий код, когда вы хотите изменить значок приложения

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

Ответ 9

Чтобы получить решение от Маркуса, мне понадобился первый Intent, поэтому:

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