在 React Native 中,如何更改 Android 应用程序图标 "dynamically"?

in React Native, How to change Android app icon "dynamically"?

我在互联网上查找并发现了一些像 this which doesn't support React Native Android and this 这样的软件包,但没有很好的记录...

React Native Android 还有其他模块吗?或者有什么方法可以让应用程序动态更改图标。

例如,当系统为深色主题时,图标变为深色主题。或者像日历图标更改一样在图标上显示当前日期。或者随着秒数的流逝显示指针移动的时钟,等等。

在 Android 上启用动态图标更改的步骤很少。 看似工作量很大,其实很简单.

1.将所有图标放在 mipmap 文件夹中。

2。 AndroidManifest.xml(为每个图标创建 activity 别名)

<application> 下,Main <activity> 不应包含 intent-filter & android:exported 应设置为 true。这将使 MainActivity 始终可用而不显示快捷方式图标。

<activity
    android:name=".MainActivity"
    android:label="Test App"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
    android:launchMode="singleTask"
    android:windowSoftInputMode="adjustResize"
    android:exported="true"
>
</activity>

3。现在,对于要显示的每个图标,创建 <activity-alias>。将默认图标 android:enabled 设置为 true 所有启用的图标都将显示在桌面上。因此,请确保只启用一个 activity 别名。

<activity-alias
    android:label="Test App :: Default"
    android:icon="@mipmap/icon1"
    android:name="First"
    android:enabled="true"
    android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</activity-alias>

<activity-alias
android:label="Test App :: Special"
android:icon="@mipmap/icon2"
android:name="Second"
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>

<activity-alias
    android:label="Test App :: Very Special"
    android:icon="@mipmap/icon3"
    android:name="Third"
    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>

4.创建一个Java类

为每个 activity 别名创建空 class。

package com.your.package;

class First {
}

5.创建 IconChanger class

@ReactMethod 允许从 react-native 访问它。您可以将 com.your.package 更改为 BuildConfig.APPLICATION_ID 以避免多次写入包名。

package your.package.name;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
import android.content.pm.PackageManager;
import android.content.ComponentName;
import com.facebook.react.bridge.Promise;
import android.os.Bundle;  
import android.widget.Toast; 

public class IconChanger extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;

    public IconChanger(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    @Override
    public String getName() {
        return "IconChanger";
    }

    @ReactMethod
    public void changeIcon(String enableIntent, String disableIntent, Promise response) {
        try {
            PackageManager packageManager = this.reactContext.getPackageManager();
            int action;
            String activeIntent="com.your.package."+enableIntent;

            Toast.makeText( this.reactContext,"Enabling "+enableIntent,Toast.LENGTH_SHORT).show();

            packageManager.setComponentEnabledSetting(
                new ComponentName("com.your.package", activeIntent), 
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP
            );

            if(!disableIntent.equals(null) && !disableIntent.equals(enableIntent)){
                activeIntent="com.your.package.."+disableIntent;
                Toast.makeText( this.reactContext,"Disabling "+disableIntent,Toast.LENGTH_SHORT).show();

                packageManager.setComponentEnabledSetting(
                    new ComponentName("com.your.package.", activeIntent), 
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP
                );
            }
            response.resolve(enableIntent);
        } catch (Exception e) {
            response.reject("Error", e);
        }
    }
}

6.将其注册为 NativeModule

创建CustomPackages.java

package com.your.package;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CustomPackages implements ReactPackage {

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new IconChanger(reactContext));

        return modules;
    }
}

7.现在将此包添加到您的 MainApplication.java

@Override
protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new CustomPackages());
    return packages;
}

8.从 react-native

访问 IconChanger
import { NativeModules } from 'react-native';
const { IconChanger } = NativeModules;
IconChanger.changeIcon(newIcon, oldIcon);

[注释]

  • 这将启用新的 activity,然后关闭旧的 activity。

  • 在应用程序关闭时或在后台更改图标,因为更改 activity 会关闭应用程序。