Android 10之前的暗藏形式:

con.getPackageManager().setComponentEnabledSetting(new ComponentName(con,activityAliasName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);

Android 10之后,该暗藏图标形式生效,暗藏胜利后仍然会在桌面上保留其图标,点击后会跳转到利用详情页。具体查看官网API更新阐明:getActivityList) 。

那这就很好受了,在我看来,目前设计的并不太正当,能不能暗藏应该由用户决定,而不是强制不容许暗藏,不过目前官网也设置了管制开关,能够批改零碎设置来去掉该限度,只不过须要adb或能批改零碎设置的APP或ROOT权限能力批改。接下来就是具体分析了。

具体分析

  • getActivityList

    //frameworks/base/core/java/android/content/pm/LauncherApps.java  public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {      logErrorForInvalidProfileAccess(user);      try {          return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),                  packageName, user), user);      } catch (RemoteException re) {          throw re.rethrowFromSystemServer();      }  }
  • getLauncherActivities

    //frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java@Overridepublic ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(      String callingPackage, String packageName, UserHandle user) throws RemoteException {  //查问蕴含ACTION_MAIN和CATEGORY_LAUNCHER的activity  ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =          queryActivitiesForUser(callingPackage,                  new Intent(Intent.ACTION_MAIN)                          .addCategory(Intent.CATEGORY_LAUNCHER)                          .setPackage(packageName),                  user);  //检测零碎设置(零碎设置管制开关管制是否走新版限度逻辑)  if (Settings.Global.getInt(mContext.getContentResolver(),          Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {      return launcherActivities;  }  //没有找到入口activity  if (launcherActivities == null) {      // Cannot access profile, so we don't even return any hidden apps.      return null;  }  final int callingUid = injectBinderCallingUid();  final long ident = injectClearCallingIdentity();  try {      //托管配置文件能够暗藏APP      if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {          // Managed profile should not show hidden apps          return launcherActivities;      }      //设施管理员能够暗藏APP      if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {          // Device owner devices should not show hidden apps          return launcherActivities;      }      final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(              launcherActivities.getList());      if (packageName != null) {          // If this hidden app should not be shown, return the original list.          // Otherwise, inject hidden activity that forwards user to app details page.          //可能是如果大于0,示意有无效的启动入口,则不必注入暗藏的APP详情页入口          if (result.size() > 0) {              return launcherActivities;          }          final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(                  packageName, /* flags= */ 0, callingUid, user.getIdentifier());          //依据APP信息进行判断是否注入暗藏APP详情页入口          if (shouldShowSyntheticActivity(user, appInfo)) {              LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,                      callingUid, user);              if (info != null) {                  result.add(info);              }          }          return new ParceledListSlice<>(result);      }      final HashSet<String> visiblePackages = new HashSet<>();      for (LauncherActivityInfoInternal info : result) {          visiblePackages.add(info.getActivityInfo().packageName);      }      final List<ApplicationInfo> installedPackages =              mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,                      user.getIdentifier(), callingUid);      for (ApplicationInfo applicationInfo : installedPackages) {          if (!visiblePackages.contains(applicationInfo.packageName)) {              if (!shouldShowSyntheticActivity(user, applicationInfo)) {                  continue;              }              LauncherActivityInfoInternal info = getHiddenAppActivityInfo(                      applicationInfo.packageName, callingUid, user);              if (info != null) {                  result.add(info);              }          }      }      return new ParceledListSlice<>(result);  } finally {      injectRestoreCallingIdentity(ident);  }}
  • shouldShowSyntheticActivity

    //frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.javaprivate boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) {  //没有app信息,是零碎APP或者更新的零碎APP,则不增加暗藏详情页入口  if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {      return false;  }  //是管理员APP也不必增加该入口  if (isManagedProfileAdmin(user, appInfo.packageName)) {      return false;  }  final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);  if (pkg == null) {      // Should not happen, but we shouldn't be failing if it does      return false;  }  // If app does not have any default enabled launcher activity or any permissions,  // the app can legitimately have no icon so we do not show the synthetic activity.  return requestsPermissions(pkg) && hasDefaultEnableLauncherActivity(          appInfo.packageName);}

    依据以上源码,能够发现该机制有个全局设置开关,并且有多种状况能够不受该机制限度。因而总结出如下解决方案:

  • 若是你的APP有adb权限或ROOT权限,能够间接批改这个全局开关,批改命令如下:

    adb shell settings put global show_hidden_icon_apps_enabled 0
  • 或者你的APP有设施管理员权限
  • 或者你的APP是零碎APP
  • 或者你的APP没有任何默认启动入口
  • 或者你的APP没有申请任何权限
  • 或者你的APP波及到Managed profile(这个具体不太分明怎么用)
  • 或者你的APP是Xposed模块,能够间接注入零碎服务拦挡相干API,或者注入能够批改零碎设置的APP(比方:设置)间接批改这个开关
  • 或者你是零碎源码开发者,能够间接批改该处代码即可
  • 其它更多还未摸索出的形式(比方利用某些逻辑破绽)........

参考

  • Android hide/unhide app icon programmatically

转自:https://blog.csdn.net/qq_2691...