How to check if an Activity is enabled?

6.8k Views Asked by At

Background

I'm trying to check if an activity (or any other app component type, for that matter) is enabled/disabled at runtime.

The problem

It's possible to use the next code:

    final ComponentName componentName = new ComponentName(context, activityClass);
    final PackageManager pm = context.getPackageManager();
    final int result = pm.getComponentEnabledSetting(componentName);

But the returned result, as written on the documentation is:

Returns the current enabled state for the component. May be one of COMPONENT_ENABLED_STATE_ENABLED, COMPONENT_ENABLED_STATE_DISABLED, or COMPONENT_ENABLED_STATE_DEFAULT. The last one means the component's enabled state is based on the original information in the manifest as found in ComponentInfo.

So it's not just enabled/disabled, but also "default".

The question

If "COMPONENT_ENABLED_STATE_DEFAULT" is returned, how do I know if it's default as enabled or disabled (at runtime)?

The reason for this question is that the code should work no matter what people put in the manifest (for the "enabled" attribute) .

Is it possible perhaps to use intents resolving?

3

There are 3 best solutions below

9
On BEST ANSWER

If COMPONENT_ENABLED_STATE_DEFAULT is returned, how do I know if it's default as enabled or disabled?

You will need to load all the components using PackageManager and check the enabled state for the matching ComponentInfo. The following code should work:

public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
  ComponentName componentName = new ComponentName(pkgName, clsName);
  int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);

  switch (componentEnabledSetting) {
    case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
      return false;
    case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
      return true;
    case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
    default:
      // We need to get the application info to get the component's default state
      try {
        PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
            | PackageManager.GET_RECEIVERS
            | PackageManager.GET_SERVICES
            | PackageManager.GET_PROVIDERS
            | PackageManager.GET_DISABLED_COMPONENTS);

        List<ComponentInfo> components = new ArrayList<>();
        if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
        if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
        if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);

        for (ComponentInfo componentInfo : components) {
          if (componentInfo.name.equals(clsName)) {
            return componentInfo.isEnabled();
          }
        }

        // the component is not declared in the AndroidManifest
        return false;
      } catch (PackageManager.NameNotFoundException e) {
        // the package isn't installed on the device
        return false;
      }
  }
}

Testing the above code on my device:

System.out.println(isComponentEnabled(getPackageManager(),
    "com.android.systemui",
    "com.android.systemui.DessertCaseDream"));

System.out.println(isComponentEnabled(getPackageManager(),
    "com.android.settings",
    "com.android.settings.DevelopmentSettings"));

false

true

7
On

I think the field ComponentInfo.enabled means the value set in the AndroidManifest.xml file. If not specified, the default will be true. So, in the following example:

<receiver android:name=".BroadcastReceiver1"
            android:enabled="false" />

<receiver android:name=".BroadcastReceiver2"
            android:enabled="true" />

<receiver android:name=".BroadcastReceiver3" />

the enabled field will be false in BroadcastReceiver1, true in BroadcastReceiver2 and true in BroadcastReceiver3, while pm.getComponentEnabledSetting(componentName) will return ENABLED or DISABLED if the value in AndroidManifest is overridden or DEFAULT if not overriden

Then, according to @Jared Rummler answer if pm.getComponentEnabledSetting(componentName) returns PackageManager.COMPONENT_ENABLED_STATE_DEFAULT the actual value will be the one set in ComponentInfo.enabled field which will be the same as the one set in AndroidManifest, while ComponentInfo.isEnabled() would depend on the whole application state

EDIT: To sum up:

public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
  ComponentName componentName = new ComponentName(pkgName, clsName);
  int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);

  switch (componentEnabledSetting) {
    case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
      return false;
    case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
      return true;
    case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
    default:
      // We need to get the application info to get the component's default state
      try {
        PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
            | PackageManager.GET_RECEIVERS
            | PackageManager.GET_SERVICES
            | PackageManager.GET_PROVIDERS
            | PackageManager.GET_DISABLED_COMPONENTS);

        List<ComponentInfo> components = new ArrayList<>();
        if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
        if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
        if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);

        for (ComponentInfo componentInfo : components) {
          if (componentInfo.name.equals(clsName)) {
            return componentInfo.enabled; //This is the default value (set in AndroidManifest.xml)
            //return componentInfo.isEnabled(); //Whole package dependant
          }
        }

        // the component is not declared in the AndroidManifest
        return false;
      } catch (PackageManager.NameNotFoundException e) {
        // the package isn't installed on the device
        return false;
      }
  }
}
7
On

The code of Jared Rummler is good and it work but it takes the status in the manifest.

To have the current status of a component inside my app I needed to create the "ComponentName" through the "Package Context" and the "Component Class" and not the "Package Name" and "Class Name".

I had a BroadcastReceiver setted to "enabled = false" in the manifest, after that I enabled it inside my app using the package manager, but the Jared Rummler's codes always return me "STATE_ENABLED_DEFAULT" and "enabled and isEnabled()" always returned false. Using the "Package Context" and the "Component Class" i get directly the "ENABLED_STATE_DISABLED" and "ENABLED_STATE_ENABLED" without using the code in the default part, also the "enabled and isEnabled()" returned me anyway FALSE if I've started the receiver using the package manager.

Hope this is useful, see u

public static void enableDisableComponent(Context pckg, Class componentClass, boolean enable){
ComponentName component = new ComponentName(pckg, componentClass);
if(enable == !checkIfComponentIsEnabled(pckg, componentClass)) {
    pckg.getPackageManager().setComponentEnabledSetting(
        component,
        enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP
    );
}

}

public static boolean checkIfComponentIsEnabled(Context pckg, Class componentClass){
            boolean ret = false;
            ComponentName componentName = new ComponentName(pckg, componentClass);
            int componentEnabled = pckg.getPackageManager().getComponentEnabledSetting(componentName);
            switch(componentEnabled){
                case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
                    break;
                case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
                    ret = true;
                    break;
                case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
                default:
                    ret = checkEnabledComponentsInfo(pckg, componentClass);
                    break;
            }
            return ret;
        }


private static boolean checkEnabledComponentsInfo(Context pckg, Class componentClass){
    boolean ret = false;
    try{
        PackageInfo packageInfo = pckg.getPackageManager().getPackageInfo(
            pckg.getPackageName(),
            PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS |
                PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS |
                PackageManager.GET_DISABLED_COMPONENTS
        );
        List<ComponentInfo> componentsInfo = new ArrayList<>();
        if(packageInfo.activities != null && packageInfo.activities.length > 0){
            Collections.addAll(componentsInfo, packageInfo.activities);
        }
        if(packageInfo.services != null && packageInfo.services.length > 0){
            Collections.addAll(componentsInfo, packageInfo.services);
        }
        if(packageInfo.providers != null && packageInfo.providers.length > 0){
            Collections.addAll(componentsInfo, packageInfo.providers);
        }
        if(packageInfo.receivers != null && packageInfo.receivers.length > 0){
            Collections.addAll(componentsInfo, packageInfo.receivers);
        }
        if(componentsInfo.size() > 0){
            for(ComponentInfo info : componentsInfo){
                if(info.name.equals(componentClass.getName())){
                    ret = info.isEnabled();
                    break;
                }
            }
        }
    } catch(PackageManager.NameNotFoundException nnfE){
        onException(nnfE);
    }
    return ret;
}