修改思路 1、增加全局控制变量 sys.launcher3.is_full_app,用来动态切换 2、增加两套布局,对应有抽屉和无抽屉 3、去除 allAppsButton 4、将 AllAppsContainerView 中的图标加载到 Workspace 5、新安装的 app 自动添加图标到 Workspace 6、替换 Workspace 图标长按删除选项为取消 7、屏蔽上拉显示抽屉页面手势 8、修改页面指示线为圆点 上代码 1、增加全局控制变量 sys.launcher3.is_full_app 1) 在 LauncherAppState 中增加静态方法 isDisableAllApps(), 通过修改 Settings 中自定义的值 sys.launcher3.is_full_app vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\LauncherAppState.java private static Context mContext; public static boolean isDisableAllApps() { if (mContext != null) { return Settings.System.getInt(mContext.getContentResolver(), "sys.launcher3.is_full_app", 0) == 1; } return true; } vendor\mediatek\proprietary\packages\apps\Launcher3\AndroidManifest-common.xml 2) AndroidManifest-common.xml 中增加权限 3) 在 SettingsActivity 中增加 SwitchPreference 用以动态修改 sys.launcher3.is_full_app vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\SettingsActivity.java 在内部类 LauncherSettingsFragment 中重写 onPreferenceTreeClick() 用以监听 SwitchPreference 点击 /** * This fragment shows the launcher preferences. */ public static class LauncherSettingsFragment extends PreferenceFragment { ..... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); .... //读取保存的值,初始化 SwitchPreference 的初始状态,是否选中 int isFull = Settings.System.getInt(getActivity().getContentResolver(), "sys.launcher3.is_full_app", 0); Log.d("Launcher3", "sys.launcher3.is_full_app="+isFull); SwitchPreference fullSwitch = (SwitchPreference) findPreference("pref_is_full_app"); fullSwitch.setChecked(isFull==1); } //add for change is_full_app value @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { boolean result = true; final String key = preference.getKey(); if ("pref_is_full_app".equals(key)) { boolean checked = ((SwitchPreference) preference).isChecked(); Settings.System.putInt(getActivity().getContentResolver(), "sys.launcher3.is_full_app", checked ? 1 : 0); Log.e("Launcher3", "SwitchPreference checked="+checked); // Value has changed ProgressDialog.show(getActivity(), null /* title */, getActivity().getString(R.string.full_app_override_progress), true /* indeterminate */, false /* cancelable */); new LooperExecutor(LauncherModel.getWorkerLooper()).execute( new OverrideApplyHandler(getActivity())); } return result; } } 点击 SwitchPreference 后需要保存 sys.launcher3.is_full_app 新值,同时清除 Launcher3 的缓存,延时启动并结束当前进程 清除缓存方法 clearApplicationUserData 在 Launcher3 中编译报错,所以通过发送广播到 Setting 中进行真正的清缓存操作 //add for change is_full_app value private static class OverrideApplyHandler implements Runnable { private final Context mContext; private OverrideApplyHandler(Context context) { mContext = context; } @Override public void run() { // Clear the icon cache. LauncherAppState.getInstance(mContext).getIconCache().clear(); // Wait for it try { Thread.sleep(1000); } catch (Exception e) { Log.e("Launcher3", "Error waiting", e); } // Schedule an alarm before we kill ourself. Intent homeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setPackage(mContext.getPackageName()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pi = PendingIntent.getActivity(mContext, 42, homeIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); mContext.getSystemService(AlarmManager.class).setExact( AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 50, pi); //clear data will kill process Intent intent = new Intent("com.android.action.CLEAR_APP_DATA"); intent.putExtra("pkgName", "com.android.launcher3"); intent.addFlags(0x01000000); mContext.sendBroadcast(intent); Log.i("Launcher3", "Clearing user data com.android.launcher3"); // Kill process android.os.Process.killProcess(android.os.Process.myPid()); } } 4) SettingsActivity 对应的 xml 文件修改 launcher_preferences vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\launcher_preferences.xml 对应的 string 文件就不贴了,自己增加下就行 2、增加两套布局,对应有抽屉和无抽屉 加载布局文件对应的 xml 为 vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\device_profiles.xml Launcher3 通过获取 minWidthDps 和 minHeightDps 来确定加载哪一个 profile,我的平板分辨率是 1280*800 的,增加两个 profile 节点 对应的你需要在 xml 文件下增加 4 个文件,分别是 default_workspace_tb_5x6.xml dw_hotseat_tb.xml default_workspace_tb_5x6_no_all_app.xml dw_hotseat_tb_no_all_app.xml 这样的好处是你可以自定义不同的布局文件加载内容,上面的配置含义简单说一下,分别是最小宽度、最小高度、布局的行和列、文件夹中布局行和列、图标大小、图标文字大小、HotSeat 个数,加载的布局文件 在 InvariantDeviceProfile() 判断是否需要加载 Tablet_no_all_app profile vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\InvariantDeviceProfile.java public InvariantDeviceProfile(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); Point smallestSize = new Point(); Point largestSize = new Point(); display.getCurrentSizeRange(smallestSize, largestSize); // This guarantees that width < height minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); Log.i("Launcher3.profiles", "orignalminWidthDps="+minWidthDps + " orignalminHeightDps="+minHeightDps); //add for load no_all_app xml if (LauncherAppState.isDisableAllApps()) { Log.e("Launcher3.profiles", "load no all app profiles"); //对应 device_profiles.xml 中 Tablet_no_all_app 的值 minWidthDps = 380.0f; minHeightDps = 590.0f; } ..... } 3、去除 allAppsButton vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\Hotseat.java 将 resetLayout() 中 FeatureFlags.NO_ALL_APPS_ICON 替换为 LauncherAppState.isDisableAllApps() void resetLayout(boolean hasVerticalHotseat) { mContent.removeAllViewsInLayout(); mHasVerticalHotseat = hasVerticalHotseat; InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; if (hasVerticalHotseat) { mContent.setGridSize(1, idp.numHotseatIcons); } else { mContent.setGridSize(idp.numHotseatIcons, 1); } //if (!FeatureFlags.NO_ALL_APPS_ICON) { /// add for check is need allappbutton if (!LauncherAppState.isDisableAllApps()) { // Add the Apps button Context context = getContext(); DeviceProfile grid = mLauncher.getDeviceProfile(); ... } 4、将 AllAppsContainerView 中的图标加载到 Workspace vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\LoaderTask.java run() 中增加判断,添加 verifyApplications(), 修改 InstallShortcutReceiver 中 PendingInstallShortcutInfo 为 public public void run() { synchronized (this) { // Skip fast if we are already stopped. if (mStopped) { return; } } .... // second step TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); loadAllApps(); //add for load all app on workspace if (LauncherAppState.isDisableAllApps()) { android.util.Log.e("Launcher3", "verifyApplications()"); verifyApplications(); } .... } //add for load all app on workspace private void verifyApplications() { final Context context = mApp.getContext(); ArrayList> installQueue = new ArrayList<>(); final List profiles = mUserManager.getUserProfiles(); for (UserHandle user : profiles) { final List apps = mLauncherApps.getActivityList(null, user); ArrayList added = new ArrayList(); synchronized (this) { for (LauncherActivityInfo app : apps) { InstallShortcutReceiver.PendingInstallShortcutInfo pendingInstallShortcutInfo = new InstallShortcutReceiver.PendingInstallShortcutInfo(app, context); added.add(pendingInstallShortcutInfo); installQueue.add(pendingInstallShortcutInfo.getItemInfo()); } } if (!added.isEmpty()) { mApp.getModel().addAndBindAddedWorkspaceItems(installQueue); } } } vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\BaseModelUpdateTask.java 注释 run() 中的 return @Override public final void run() { if (!mModel.isModelLoaded()) { if (DEBUG_TASKS) { Log.d(TAG, "Ignoring model task since loader is pending=" + this); } // Loader has not yet run. //annotaion for load all app on workspace // return; } execute(mApp, mDataModel, mAllAppsList); } 5、新安装的 app 自动添加图标到 Workspace vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\PackageUpdatedTask.java execute() 中增加判断,添加 updateToWorkSpace() public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) { .... final ArrayList addedOrModified = new ArrayList<>(); addedOrModified.addAll(appsList.added); //add for load new install app on workspace if (LauncherAppState.isDisableAllApps()) { android.util.Log.e("cczLauncher3", "updateToWorkSpace()"); updateToWorkSpace(context, app, appsList); } ... } //add for load new install app on workspace public void updateToWorkSpace(Context context, LauncherAppState app , AllAppsList appsList){ ArrayList> installQueue = new ArrayList<>(); final List profiles = UserManagerCompat.getInstance(context).getUserProfiles(); ArrayList added = new ArrayList(); for (UserHandle user : profiles) { final List apps = LauncherAppsCompat.getInstance(context).getActivityList(null, user); synchronized (this) { for (LauncherActivityInfo info : apps) { for (AppInfo appInfo : appsList.added) { if(info.getComponentName().equals(appInfo.componentName)){ InstallShortcutReceiver.PendingInstallShortcutInfo mPendingInstallShortcutInfo = new InstallShortcutReceiver.PendingInstallShortcutInfo(info,context); added.add(mPendingInstallShortcutInfo); installQueue.add(mPendingInstallShortcutInfo.getItemInfo()); } } } } } if (!added.isEmpty()) { app.getModel().addAndBindAddedWorkspaceItems(installQueue); } } 6、替换 Workspace 图标长按删除选项为取消 vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\DeleteDropTarget.java 在 setTextBasedOnDragSource() 、setControlTypeBasedOnDragSource()、onAccessibilityDrop() 中分别增加判断是否需要删除图标 private void setTextBasedOnDragSource(It