经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
android 9.0 Launcher3 去掉抽屉式,显示所有 app
来源:cnblogs  作者:cczheng  时间:2019/11/8 12:13:47  对本文有异议

效果图

在这里插入图片描述

修改思路

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

  1. private static Context mContext;
  2. public static boolean isDisableAllApps() {
  3. if (mContext != null) {
  4. return Settings.System.getInt(mContext.getContentResolver(),
  5. "sys.launcher3.is_full_app", 0) == 1;
  6. }
  7. return true;
  8. }

vendor\mediatek\proprietary\packages\apps\Launcher3\AndroidManifest-common.xml

2) AndroidManifest-common.xml 中增加权限

  1. <uses-permission android:name="android.permission.WRITE_SETTINGS" />

3) 在 SettingsActivity 中增加 SwitchPreference 用以动态修改 sys.launcher3.is_full_app

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\SettingsActivity.java

在内部类 LauncherSettingsFragment 中重写 onPreferenceTreeClick() 用以监听 SwitchPreference 点击

  1. /**
  2. * This fragment shows the launcher preferences.
  3. */
  4. public static class LauncherSettingsFragment extends PreferenceFragment {
  5. .....
  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. ....
  10. //读取保存的值,初始化 SwitchPreference 的初始状态,是否选中
  11. int isFull = Settings.System.getInt(getActivity().getContentResolver(),
  12. "sys.launcher3.is_full_app", 0);
  13. Log.d("Launcher3", "sys.launcher3.is_full_app="+isFull);
  14. SwitchPreference fullSwitch = (SwitchPreference) findPreference("pref_is_full_app");
  15. fullSwitch.setChecked(isFull==1);
  16. }
  17. //add for change is_full_app value
  18. @Override
  19. public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
  20. boolean result = true;
  21. final String key = preference.getKey();
  22. if ("pref_is_full_app".equals(key)) {
  23. boolean checked = ((SwitchPreference) preference).isChecked();
  24. Settings.System.putInt(getActivity().getContentResolver(), "sys.launcher3.is_full_app",
  25. checked ? 1 : 0);
  26. Log.e("Launcher3", "SwitchPreference checked="+checked);
  27. // Value has changed
  28. ProgressDialog.show(getActivity(),
  29. null /* title */,
  30. getActivity().getString(R.string.full_app_override_progress),
  31. true /* indeterminate */,
  32. false /* cancelable */);
  33. new LooperExecutor(LauncherModel.getWorkerLooper()).execute(
  34. new OverrideApplyHandler(getActivity()));
  35. }
  36. return result;
  37. }
  38. }

点击 SwitchPreference 后需要保存 sys.launcher3.is_full_app 新值,同时清除 Launcher3 的缓存,延时启动并结束当前进程

清除缓存方法 clearApplicationUserData 在 Launcher3 中编译报错,所以通过发送广播到 Setting 中进行真正的清缓存操作

  1. //add for change is_full_app value
  2. private static class OverrideApplyHandler implements Runnable {
  3. private final Context mContext;
  4. private OverrideApplyHandler(Context context) {
  5. mContext = context;
  6. }
  7. @Override
  8. public void run() {
  9. // Clear the icon cache.
  10. LauncherAppState.getInstance(mContext).getIconCache().clear();
  11. // Wait for it
  12. try {
  13. Thread.sleep(1000);
  14. } catch (Exception e) {
  15. Log.e("Launcher3", "Error waiting", e);
  16. }
  17. // Schedule an alarm before we kill ourself.
  18. Intent homeIntent = new Intent(Intent.ACTION_MAIN)
  19. .addCategory(Intent.CATEGORY_HOME)
  20. .setPackage(mContext.getPackageName())
  21. .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  22. PendingIntent pi = PendingIntent.getActivity(mContext, 42,
  23. homeIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
  24. mContext.getSystemService(AlarmManager.class).setExact(
  25. AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 50, pi);
  26. //clear data will kill process
  27. Intent intent = new Intent("com.android.action.CLEAR_APP_DATA");
  28. intent.putExtra("pkgName", "com.android.launcher3");
  29. intent.addFlags(0x01000000);
  30. mContext.sendBroadcast(intent);
  31. Log.i("Launcher3", "Clearing user data com.android.launcher3");
  32. // Kill process
  33. android.os.Process.killProcess(android.os.Process.myPid());
  34. }
  35. }

4) SettingsActivity 对应的 xml 文件修改 launcher_preferences

vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\launcher_preferences.xml

  1. <SwitchPreference
  2. android:key="pref_is_full_app"
  3. android:title="@string/is_full_app_title"
  4. android:summary="@string/is_full_app_desc"
  5. android:defaultValue="false"
  6. android:persistent="true" />

对应的 string 文件就不贴了,自己增加下就行

2、增加两套布局,对应有抽屉和无抽屉

加载布局文件对应的 xml 为 vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\device_profiles.xml

Launcher3 通过获取 minWidthDps 和 minHeightDps 来确定加载哪一个 profile,我的平板分辨率是 1280*800 的,增加两个 profile 节点

  1. <profile
  2. launcher:name="Tablet"
  3. launcher:minWidthDps="376"
  4. launcher:minHeightDps="586"
  5. launcher:numRows="4"
  6. launcher:numColumns="5"
  7. launcher:numFolderRows="4"
  8. launcher:numFolderColumns="5"
  9. launcher:iconSize="50"
  10. launcher:iconTextSize="11"
  11. launcher:numHotseatIcons="5"
  12. launcher:defaultLayoutId="@xml/default_workspace_tb_5x6"
  13. />
  14. <profile
  15. launcher:name="Tablet_no_all_app"
  16. launcher:minWidthDps="380"
  17. launcher:minHeightDps="590"
  18. launcher:numRows="4"
  19. launcher:numColumns="5"
  20. launcher:numFolderRows="4"
  21. launcher:numFolderColumns="5"
  22. launcher:iconSize="50"
  23. launcher:iconTextSize="11"
  24. launcher:numHotseatIcons="4"
  25. launcher:defaultLayoutId="@xml/default_workspace_tb_5x6_no_all_app"
  26. />

对应的你需要在 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

  1. public InvariantDeviceProfile(Context context) {
  2. WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  3. Display display = wm.getDefaultDisplay();
  4. DisplayMetrics dm = new DisplayMetrics();
  5. display.getMetrics(dm);
  6. Point smallestSize = new Point();
  7. Point largestSize = new Point();
  8. display.getCurrentSizeRange(smallestSize, largestSize);
  9. // This guarantees that width < height
  10. minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
  11. minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
  12. Log.i("Launcher3.profiles", "orignalminWidthDps="+minWidthDps + " orignalminHeightDps="+minHeightDps);
  13. //add for load no_all_app xml
  14. if (LauncherAppState.isDisableAllApps()) {
  15. Log.e("Launcher3.profiles", "load no all app profiles");
  16. //对应 device_profiles.xml 中 Tablet_no_all_app 的值
  17. minWidthDps = 380.0f;
  18. minHeightDps = 590.0f;
  19. }
  20. .....
  21. }

3、去除 allAppsButton

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\Hotseat.java

将 resetLayout() 中 FeatureFlags.NO_ALL_APPS_ICON 替换为 LauncherAppState.isDisableAllApps()

  1. void resetLayout(boolean hasVerticalHotseat) {
  2. mContent.removeAllViewsInLayout();
  3. mHasVerticalHotseat = hasVerticalHotseat;
  4. InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
  5. if (hasVerticalHotseat) {
  6. mContent.setGridSize(1, idp.numHotseatIcons);
  7. } else {
  8. mContent.setGridSize(idp.numHotseatIcons, 1);
  9. }
  10. //if (!FeatureFlags.NO_ALL_APPS_ICON) {
  11. /// add for check is need allappbutton
  12. if (!LauncherAppState.isDisableAllApps()) {
  13. // Add the Apps button
  14. Context context = getContext();
  15. DeviceProfile grid = mLauncher.getDeviceProfile();
  16. ...
  17. }

4、将 AllAppsContainerView 中的图标加载到 Workspace

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\LoaderTask.java

run() 中增加判断,添加 verifyApplications(), 修改 InstallShortcutReceiver 中 PendingInstallShortcutInfo 为 public

  1. public void run() {
  2. synchronized (this) {
  3. // Skip fast if we are already stopped.
  4. if (mStopped) {
  5. return;
  6. }
  7. }
  8. ....
  9. // second step
  10. TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
  11. loadAllApps();
  12. //add for load all app on workspace
  13. if (LauncherAppState.isDisableAllApps()) {
  14. android.util.Log.e("Launcher3", "verifyApplications()");
  15. verifyApplications();
  16. }
  17. ....
  18. }
  19. //add for load all app on workspace
  20. private void verifyApplications() {
  21. final Context context = mApp.getContext();
  22. ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
  23. final List<UserHandle> profiles = mUserManager.getUserProfiles();
  24. for (UserHandle user : profiles) {
  25. final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
  26. ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
  27. synchronized (this) {
  28. for (LauncherActivityInfo app : apps) {
  29. InstallShortcutReceiver.PendingInstallShortcutInfo pendingInstallShortcutInfo = new InstallShortcutReceiver.PendingInstallShortcutInfo(app, context);
  30. added.add(pendingInstallShortcutInfo);
  31. installQueue.add(pendingInstallShortcutInfo.getItemInfo());
  32. }
  33. }
  34. if (!added.isEmpty()) {
  35. mApp.getModel().addAndBindAddedWorkspaceItems(installQueue);
  36. }
  37. }
  38. }

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\BaseModelUpdateTask.java

注释 run() 中的 return

  1. @Override
  2. public final void run() {
  3. if (!mModel.isModelLoaded()) {
  4. if (DEBUG_TASKS) {
  5. Log.d(TAG, "Ignoring model task since loader is pending=" + this);
  6. }
  7. // Loader has not yet run.
  8. //annotaion for load all app on workspace
  9. // return;
  10. }
  11. execute(mApp, mDataModel, mAllAppsList);
  12. }

5、新安装的 app 自动添加图标到 Workspace

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\PackageUpdatedTask.java

execute() 中增加判断,添加 updateToWorkSpace()

  1. public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
  2. ....
  3. final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
  4. addedOrModified.addAll(appsList.added);
  5. //add for load new install app on workspace
  6. if (LauncherAppState.isDisableAllApps()) {
  7. android.util.Log.e("cczLauncher3", "updateToWorkSpace()");
  8. updateToWorkSpace(context, app, appsList);
  9. }
  10. ...
  11. }
  12. //add for load new install app on workspace
  13. public void updateToWorkSpace(Context context, LauncherAppState app , AllAppsList appsList){
  14. ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
  15. final List<UserHandle> profiles = UserManagerCompat.getInstance(context).getUserProfiles();
  16. ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added
  17. = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
  18. for (UserHandle user : profiles) {
  19. final List<LauncherActivityInfo> apps = LauncherAppsCompat.getInstance(context).getActivityList(null, user);
  20. synchronized (this) {
  21. for (LauncherActivityInfo info : apps) {
  22. for (AppInfo appInfo : appsList.added) {
  23. if(info.getComponentName().equals(appInfo.componentName)){
  24. InstallShortcutReceiver.PendingInstallShortcutInfo mPendingInstallShortcutInfo
  25. = new InstallShortcutReceiver.PendingInstallShortcutInfo(info,context);
  26. added.add(mPendingInstallShortcutInfo);
  27. installQueue.add(mPendingInstallShortcutInfo.getItemInfo());
  28. }
  29. }
  30. }
  31. }
  32. }
  33. if (!added.isEmpty()) {
  34. app.getModel().addAndBindAddedWorkspaceItems(installQueue);
  35. }
  36. }

6、替换 Workspace 图标长按删除选项为取消

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\DeleteDropTarget.java

在 setTextBasedOnDragSource() 、setControlTypeBasedOnDragSource()、onAccessibilityDrop() 中分别增加判断是否需要删除图标

  1. private void setTextBasedOnDragSource(ItemInfo item) {
  2. if (!TextUtils.isEmpty(mText)) {
  3. mText = getResources().getString(item.id != ItemInfo.NO_ID
  4. ? R.string.remove_drop_target_label
  5. : android.R.string.cancel);
  6. //add for hide deletedroptarget
  7. if (LauncherAppState.isDisableAllApps()) {
  8. android.util.Log.e("Launcher3", "hide delete drop target");
  9. mText = getResources().getString(isCanDrop(item)
  10. ? R.string.remove_drop_target_label
  11. : android.R.string.cancel);
  12. }
  13. requestLayout();
  14. }
  15. }
  16. private void setControlTypeBasedOnDragSource(ItemInfo item) {
  17. mControlType = item.id != ItemInfo.NO_ID ? ControlType.REMOVE_TARGET
  18. : ControlType.CANCEL_TARGET;
  19. //add for hide deletedroptarget [S]
  20. if (LauncherAppState.isDisableAllApps()) {
  21. mControlType = isCanDrop(item) ? ControlType.REMOVE_TARGET
  22. : ControlType.CANCEL_TARGET;
  23. }
  24. }
  25. public void onAccessibilityDrop(View view, ItemInfo item) {
  26. // Remove the item from launcher and the db, we can ignore the containerInfo in this call
  27. // because we already remove the drag view from the folder (if the drag originated from
  28. // a folder) in Folder.beginDrag()
  29. //add if juge is need remove item from workspace
  30. if (!LauncherAppState.isDisableAllApps() || isCanDrop(item)) {
  31. mLauncher.removeItem(view, item, true /* deleteFromDb */);
  32. mLauncher.getWorkspace().stripEmptyScreens();
  33. mLauncher.getDragLayer()
  34. .announceForAccessibility(getContext().getString(R.string.item_removed));
  35. }
  36. }
  37. private boolean isCanDrop(ItemInfo item){
  38. return !(item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
  39. item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
  40. }

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\dragndrop\DragController.java

drop() 中增加判断,取消当前拖拽操作

  1. private void drop(DropTarget dropTarget, Runnable flingAnimation) {
  2. ....
  3. boolean accepted = false;
  4. if (dropTarget != null) {
  5. dropTarget.onDragExit(mDragObject);
  6. if (dropTarget.acceptDrop(mDragObject)) {
  7. if (flingAnimation != null) {
  8. flingAnimation.run();
  9. } else {
  10. dropTarget.onDrop(mDragObject, mOptions);
  11. }
  12. accepted = true;
  13. //add for cancel canceldroptarget handle
  14. if (LauncherAppState.isDisableAllApps() && dropTarget instanceof DeleteDropTarget &&
  15. isNeedCancelDrag(mDragObject.dragInfo)) {
  16. cancelDrag();
  17. }
  18. }
  19. }
  20. ...
  21. }
  22. private boolean isNeedCancelDrag(ItemInfo item){
  23. return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
  24. item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
  25. }

7、屏蔽上拉显示抽屉页面手势

vendor\mediatek\proprietary\packages\apps\Launcher3\quickstep\src\com\android\launcher3\uioverrides\OverviewToAllAppsTouchController.java

canInterceptTouch() 中增加判断是否直接拦截

  1. @Override
  2. protected boolean canInterceptTouch(MotionEvent ev) {
  3. //add for forbidden workspace drag change GradientView alph
  4. if (LauncherAppState.isDisableAllApps()){
  5. android.util.Log.e("Launcher3", "canInterceptTouch()");
  6. return false;
  7. }
  8. if (mCurrentAnimation != null) {
  9. // If we are already animating from a previous state, we can intercept.
  10. return true;
  11. }
  12. if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
  13. return false;
  14. }
  15. if (mLauncher.isInState(ALL_APPS)) {
  16. // In all-apps only listen if the container cannot scroll itself
  17. return mLauncher.getAppsView().shouldContainerScroll(ev);
  18. } else if (mLauncher.isInState(NORMAL)) {
  19. return true;
  20. } else if (mLauncher.isInState(OVERVIEW)) {
  21. RecentsView rv = mLauncher.getOverviewPanel();
  22. return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
  23. } else {
  24. return false;
  25. }
  26. }

8、修改页面指示线为圆点

vendor\mediatek\proprietary\packages\apps\Launcher3\res\layout\launcher.xml

WorkspacePageIndicator 改为 PageIndicatorDots

  1. <com.android.launcher3.pageindicators.PageIndicatorDots
  2. android:id="@+id/page_indicator"
  3. android:layout_width="match_parent"
  4. android:layout_height="4dp"
  5. android:layout_gravity="bottom|center_horizontal"
  6. android:theme="@style/HomeScreenElementTheme" />

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\pageindicators\PageIndicatorDots.java

增加 PageIndicatorDots 继承 Insettable,复写setInsets(), 调整圆点的位置

  1. public class PageIndicatorDots extends View implements PageIndicator, Insettable {
  2. // add for change WorkspacePageIndicator line to dot
  3. @Override
  4. public void setInsets(Rect insets) {
  5. DeviceProfile grid = mLauncher.getDeviceProfile();
  6. FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
  7. if (grid.isVerticalBarLayout()) {
  8. Rect padding = grid.workspacePadding;
  9. lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
  10. lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
  11. lp.bottomMargin = padding.bottom;
  12. } else {
  13. lp.leftMargin = lp.rightMargin = 0;
  14. lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
  15. lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
  16. }
  17. setLayoutParams(lp);
  18. }
  19. @Override
  20. public void setScroll(int currentScroll, int totalScroll) {
  21. if (mNumPages > 1) {
  22. if (mIsRtl) {
  23. currentScroll = totalScroll - currentScroll;
  24. }
  25. int scrollPerPage = totalScroll / (mNumPages - 1);
  26. // add for change WorkspacePageIndicator line to dot
  27. if (scrollPerPage == 0) {
  28. return;
  29. }
  30. int pageToLeft = currentScroll / scrollPerPage;
  31. int pageToLeftScroll = pageToLeft * scrollPerPage;
  32. int pageToRightScroll = pageToLeftScroll + scrollPerPage;
  33. ...
  34. }

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\states\SpringLoadedState.java

注释 setShouldAutoHide(),避免长按 workSpace 时发生崩溃

  1. @Override
  2. public void onStateEnabled(Launcher launcher) {
  3. Workspace ws = launcher.getWorkspace();
  4. ws.showPageIndicatorAtCurrentScroll();
  5. //annotaion for WorkspacePageIndicator line to dot
  6. // ws.getPageIndicator().setShouldAutoHide(false);
  7. // Prevent any Un/InstallShortcutReceivers from updating the db while we are
  8. // in spring loaded mode
  9. InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
  10. launcher.getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
  11. }
  12. @Override
  13. public void onStateDisabled(final Launcher launcher) {
  14. //annotaion for WorkspacePageIndicator line to dot
  15. // launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
  16. // Re-enable any Un/InstallShortcutReceiver and now process any queued items
  17. InstallShortcutReceiver.disableAndFlushInstallQueue(
  18. InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
  19. }

欢迎关注我的英文公众号【十句英文】,每日1首英文金曲+10句英文,伴你共同进步。

微信扫一扫下方二维码即可关注:

ME71HS.jpg

原文链接:http://www.cnblogs.com/cczheng-666/p/11818825.html

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号