1 Requirements Overview
When the business requirement requires that the main page does not display one or more specific apks, we need to hide this apk in the launcher. This article takes hiding the three apks of Aqua Mail, Calculator, and FileCommander as examples to explain in detail.
2 Implement the core class of functions
Code Path
packages\apps\Launcher3\src\com\android\launcher3\model\LoaderTask.java
packages\apps\Launcher3\quickstep\src\com\android\launcher3\appprediction\PredictionRowView.java
3 Core code analysis
3.1 LoaderTask loading allapps process
Loading the apk list is loaded in LoaderTask, we only need to shield the apk required by our business at the place where all apk lists are loaded to realize this function. Look at the run() method
public void run() { synchronized (this) { // Skip fast if we are already stopped. if (mStopped) { return; } } Object traceToken = TraceHelper.INSTANCE.beginSection(TAG); TimingLogger logger = new TimingLogger(TAG, "run"); LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List<ShortcutInfo> allShortcuts = new ArrayList<>(); Trace.beginSection("LoadWorkspace"); try { loadWorkspace(allShortcuts, memoryLogger); } finally { Trace. endSection(); } logASplit(logger, "loadWorkspace"); // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. // sanitizeData should not be invoked if the workspace is loaded from a db different // from the main db as defined in the invariant device profile. // (e.g. both grid preview and minimal device mode uses a different db) if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) { verifyNotStopped(); sanitizeData(); logASplit(logger, "sanitizeData"); } verifyNotStopped(); mResults.bindWorkspace(true /* incrementBindId */); logASplit(logger, "bindWorkspace"); mModelDelegate.workspaceLoadComplete(); // Notify the installer packages of packages with active installs on the first screen. sendFirstScreenActiveInstallsBroadcast(); logASplit(logger, "sendFirstScreenActiveInstallsBroadcast"); //Take a break waitForIdle(); logASplit(logger, "step 1 complete"); verifyNotStopped(); // second step Trace.beginSection("LoadAllApps"); List<LauncherActivityInfo> allActivityList; try { allActivityList = loadAllApps(); } finally { Trace. endSection(); } logASplit(logger, "loadAllApps"); verifyNotStopped(); mResults. bindAllApps(); logASplit(logger, "bindAllApps"); verifyNotStopped(); IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); setIgnorePackages(updateHandler); updateHandler. updateIcons(allActivityList, LauncherActivityCachingLogic.newInstance(mApp.getContext()), mApp.getModel()::onPackageIconsUpdated); logASplit(logger, "update icon cache"); if (FeatureFlags. ENABLE_DEEP_SHORTCUT_ICON_CACHE. get()) { verifyNotStopped(); logASplit(logger, "save shortcuts in icon cache"); updateHandler. updateIcons(allShortcuts, new ShortcutCachingLogic(), mApp.getModel()::onPackageIconsUpdated); } //Take a break waitForIdle(); logASplit(logger, "step 2 complete"); verifyNotStopped(); // third step List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts(); logASplit(logger, "loadDeepShortcuts"); verifyNotStopped(); mResults.bindDeepShortcuts(); logASplit(logger, "bindDeepShortcuts"); if (FeatureFlags. ENABLE_DEEP_SHORTCUT_ICON_CACHE. get()) { verifyNotStopped(); logASplit(logger, "save deep shortcuts in icon cache"); updateHandler. updateIcons(allDeepShortcuts, new ShortcutCachingLogic(), (pkgs, user) -> { }); } //Take a break waitForIdle(); logASplit(logger, "step 3 complete"); verifyNotStopped(); // fourth step List<ComponentWithLabelAndIcon> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null); logASplit(logger, "load widgets"); verifyNotStopped(); mResults.bindWidgets(); logASplit(logger, "bindWidgets"); verifyNotStopped(); updateHandler. updateIcons(allWidgetsList, new ComponentWithIconCachingLogic(mApp. getContext(), true), mApp.getModel()::onWidgetLabelsUpdated); logASplit(logger, "save widgets in icon cache"); // fifth step if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { loadFolderNames(); } verifyNotStopped(); updateHandler. finish(); logASplit(logger, "finish icon update"); mModelDelegate. modelLoadComplete(); transaction.commit(); memoryLogger. clearLogs(); } catch (CancellationException e) { // Loader stopped, ignore logASplit(logger, "Cancelled"); } catch (Exception e) { memoryLogger. printLogs(); throw e; } finally { logger. dumpToLog(); } TraceHelper.INSTANCE.endSection(traceToken); }
Follow up in this method and find loadAllApps(), here is the specific method of loading all apks, continue to track
private List<LauncherActivityInfo> loadAllApps() { final List<UserHandle> profiles = mUserCache. getUserProfiles(); List<LauncherActivityInfo> allActivityList = new ArrayList<>(); // Clear the list of apps mBgAllAppsList. clear(); List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>(); for (UserHandle user : profiles) { // Query for the set of apps final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps. isEmpty()) { return allActivityList; } boolean quietMode = mUserManagerState.isUserQuiet(user); //Create the ApplicationInfos for (int i = 0; i < apps. size(); i ++ ) { LauncherActivityInfo app = apps. get(i); AppInfo appInfo = new AppInfo(app, user, quietMode); boolean isHideApk = HideApkUtils.getInstall().isHidedApkPackageName(app.getComponentName().getPackageName()); if(isHideApk){ continue; } iconRequestInfos.add(new IconRequestInfo<>( appInfo, app, /* useLowResIcon= */ false)); mBgAllAppsList.add( appInfo, app, !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()); } allActivityList. addAll(apps); } if (FeatureFlags. PROMISE_APPS_IN_ALL_APPS. get()) { // get all active sessions and add them to the all apps list for (PackageInstaller.SessionInfo info : mSessionHelper. getAllVerifiedSessions()) { AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp( mApp. getContext(), PackageInstallInfo. fromInstallingState(info), !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()); if (promiseAppInfo != null) { iconRequestInfos.add(new IconRequestInfo<>( promiseAppInfo, /* launcherActivityInfo= */ null, promiseAppInfo. usingLowResIcon())); } } } if (FeatureFlags. ENABLE_BULK_ALL_APPS_ICON_LOADING. get()) { Trace.beginSection("LoadAllAppsIconsInBulk"); try { mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); iconRequestInfos.forEach(iconRequestInfo -> mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo)); } finally { Trace. endSection(); } } mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, mUserManagerState.isAnyProfileQuietModeEnabled()); mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION, hasShortcutsPermission(mApp. getContext())); mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION, mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE") == PackageManager. PERMISSION_GRANTED); mBgAllAppsList.getAndResetChangeFlag(); return allActivityList; }
This is to load all the apk logic, we only need to add the business logic we need to hide.
// Create the ApplicationInfos for (int i = 0; i < apps. size(); i ++ ) { LauncherActivityInfo app = apps. get(i); AppInfo appInfo = new AppInfo(app, user, quietMode); boolean isHideApk = HideApkUtils.getInstall().isHidedApkPackageName(app.getComponentName().getPackageName()); if(isHideApk){ continue; } iconRequestInfos.add(new IconRequestInfo<>( appInfo, app, /* useLowResIcon= */ false)); mBgAllAppsList.add( appInfo, app, !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()); } allActivityList. addAll(apps); }
The red mark is the business logic we added, and this function is realized. However, it should be noted that in the list of apks displayed on each screen and Hotseat, do not add the apk we need to hide into this list (how to configure the apk displayed on each screen and the apk displayed on Hotseat , please read the series of articles to explain in detail)
The following is the HideApkUtils.java class
package com.android.launcher3.util; import java.util.LinkedList; import java.util.List; /** *by Hogan 2023.3.17 * Function: hide the specified apk list */ public class HideApkUtils { private static final List<String> mHiddenPackageMap = new LinkedList<>(); private HideApkUtils() { addHideAppList(); } //The apk package name that needs to be hidden, add it here private void addHideAppList() { mHiddenPackageMap.add("org.kman.AquaMail"); mHiddenPackageMap.add("com.google.android.calculator"); mHiddenPackageMap.add("com.mobisystems.fileman"); // mHiddenPackageMap.add("com.mobisystems.office"); // mHiddenPackageMap.add("com.hht.factory"); } private static class HideApkUtilsHolder { private static final HideApkUtils INSTALL = new HideApkUtils(); } public static HideApkUtils getInstall() { return HideApkUtilsHolder. INSTALL; } public boolean isHidedApkPackageName(String apkPkg) { return mHiddenPackageMap.contains(apkPkg); } }
3.2 PredictionRowView loads and displays the search prediction row view process under the search box
1. Find the setPredictedApps() method, which is used to set the content of the prediction option and update the view:
public void setPredictedApps(List<ItemInfo> items) { if (!FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get() & amp; & amp; !mActivityContext.isBindingItems() & amp; & amp; isShown() & amp; & amp; getWindowVisibility() == View.VISIBLE) { mPendingPredictedItems = items; return; } applyPredictedApps(items); }
In this method, the applyPredictedApps() method is called
private void applyPredictedApps(List<ItemInfo> items) { List<ItemInfo> itemInfoList = new ArrayList<>(); for(int i=0;i<items. size();i + + ){ String pkg = items. get(i). getTargetPackage(); boolean isHideApk = HideApkUtils.getInstall().isHidedApkPackageName(pkg); if(isHideApk){ continue; } itemInfoList.add(items.get(i)); } mPendingPredictedItems = null; mPredictedApps. clear(); mPredictedApps.addAll(itemInfoList.stream() .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo) .map(itemInfo -> (WorkspaceItemInfo) itemInfo).collect(Collectors.toList())); applyPredictionApps(); }
This is exactly loading search prediction apk, we can add business logic code here:
private void applyPredictedApps(List<ItemInfo> items) { List<ItemInfo> itemInfoList = new ArrayList<>(); for(int i=0;i<items. size();i + + ){ String pkg = items. get(i). getTargetPackage(); boolean isHideApk = HideApkUtils.getInstall().isHidedApkPackageName(pkg); if(isHideApk){ continue; } itemInfoList.add(items.get(i)); } mPendingPredictedItems = null; mPredictedApps. clear(); mPredictedApps.addAll(itemInfoList.stream() .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo) .map(itemInfo -> (WorkspaceItemInfo) itemInfo).collect(Collectors.toList())); applyPredictionApps(); }
The code marked in red is where the modification was added.
4 Complete the business rendering
4.1 The previous picture is not hidden, and the red line marks the apk that needs to be hidden
4.2 Effect picture after hiding