android 8.1 Launcher3 去掉抽屉式,显示所有 app

效果图

[图片走丢了]

修改思路

1、增加全局控制变量 sys.launcher3.is_full_app,用来动态切换

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

3、去除 allAppsButton

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

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

6、禁止 Workspace 图标长按删除选项

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

8、Workspace 第一屏禁止增加图标

上代码

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

  1. AndroidManifest-common.xml 中增加权限

  2. 在 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());
        }
    }

  1. SettingsActivity 对应的 xml 文件修改 launcher_preferences

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

 <SwitchPreference
        android:key="pref_is_full_app"
        android:title="@string/is_full_app_title"
        android:summary="@string/is_full_app_desc"
        android:defaultValue="false"
        android:persistent="true" />

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

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

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

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

 <profile
        launcher:name="Tablet"
        launcher:minWidthDps="376"
        launcher:minHeightDps="586"
        launcher:numRows="4"
        launcher:numColumns="5"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="5"
        launcher:iconSize="50"
        launcher:iconTextSize="11"
        launcher:numHotseatIcons="5"
        launcher:defaultLayoutId="@xml/default_workspace_tb_5x6"
        />

    <profile
        launcher:name="Tablet_no_all_app"
        launcher:minWidthDps="380"
        launcher:minHeightDps="590"
        launcher:numRows="4"
        launcher:numColumns="5"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="5"
        launcher:iconSize="50"
        launcher:iconTextSize="11"
        launcher:numHotseatIcons="4"
        launcher:defaultLayoutId="@xml/default_workspace_tb_5x6_no_all_app"
        />

对应的你需要在 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<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
        final List<UserHandle> profiles = mUserManager.getUserProfiles();
        for (UserHandle user : profiles) {
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
            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<AppInfo> 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<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
        final List<UserHandle> profiles = UserManagerCompat.getInstance(context).getUserProfiles();
        ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added 
        = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
        
        for (UserHandle user : profiles) {
            final List<LauncherActivityInfo> 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);
                        }
                    }
                }
            }
        }
        if (!added.isEmpty()) {
            app.getModel().addAndBindAddedWorkspaceItems(
                    new InstallShortcutReceiver.LazyShortcutsProvider(context.getApplicationContext(), added));
        }
    }

6、禁止 Workspace 图标长按删除选项

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

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

方法一、直接将 supportsDrop() 返回 false

@Override
    protected boolean supportsDrop(DragSource source, ItemInfo info) {
        //return true;
        return false;// change false
    }

方法二、拖拽至删除区域放手时不处理

//public void setTextBasedOnDragSource(DragSource dragSource) {
public void setTextBasedOnDragSource(DropTarget.DragObject dragObject) {
        if (!TextUtils.isEmpty(mText)) {
            mText = getResources().getString(/*dragSource*/dragObject.dragSource.supportsDeleteDropTarget()
                    ? R.string.remove_drop_target_label
                    : android.R.string.cancel);
            //add for hide deletedroptarget
            if (LauncherAppState.isDisableAllApps()) {
                android.util.Log.e("Launcher3", "hide delete drop target");
                mText = getResources().getString(isCanDrop(dragObject.dragInfo)
                            ? R.string.remove_drop_target_label
                            : android.R.string.cancel);
            }
            requestLayout();
        }
    }

public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
        // Remove the item from launcher and the db, we can ignore the containerInfo in this call
        // because we already remove the drag view from the folder (if the drag originated from
        // a folder) in Folder.beginDrag()
		//add if juge is need remove item from workspace
         if (!LauncherAppState.isDisableAllApps() || isCanDrop(item)) {
            launcher.removeItem(view, item, true /* deleteFromDb */);
            launcher.getWorkspace().stripEmptyScreens();
            launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
        }
    }

    private static boolean isCanDrop(ItemInfo item){
        return !(item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
    }

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

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

private void drop(DropTarget dropTarget, Runnable flingAnimation) {
	....

	boolean accepted = false;
        if (dropTarget != null) {
            dropTarget.onDragExit(mDragObject);
            if (dropTarget.acceptDrop(mDragObject)) {
                if (flingAnimation != null) {
                    flingAnimation.run();
                } else {
                    dropTarget.onDrop(mDragObject, mOptions);
                }
                accepted = true;

                //add for cancel canceldroptarget handle
                if (LauncherAppState.isDisableAllApps() && dropTarget instanceof DeleteDropTarget &&
                        isNeedCancelDrag(mDragObject.dragInfo)) {
                    cancelDrag();
                }
            }
        }
		...
}

private boolean isNeedCancelDrag(ItemInfo item){
        return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
 }

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

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

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

	@Override
    public boolean onDrag(float displacement, float velocity) {
        Log.e("Launcher3", "onDrag()");
        if (true)  return true;
        //add for forbidden workspace drag change GradientView alph

        if (mAppsView == null) {
            return false;   // early termination.
        }

        mContainerVelocity = velocity;

        float shift = Math.min(Math.max(0, mShiftStart + displacement), mShiftRange);
        setProgress(shift / mShiftRange);

        return true;
    }

    @Override
    public void onDragEnd(float velocity, boolean fling) {
        Log.e("Launcher3", "onDragEnd()");
        //add for forbidden workspace drag change GradientView alph
        if (true){
            if (velocity < 0) {
                mLauncher.showWorkspace(true);
            }
            return;
        }  
        //add for forbidden workspace drag change GradientView alph

        if (mAppsView == null) {
            return; // early termination.
        }

        final int containerType = mTouchEventStartedOnHotseat
                ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
		.....

}

8、Workspace 第一屏禁止增加图标

vendor\mediatek\proprietary\packages\apps\LauncherSip\src\com\android\launcher3\Workspace.java

图标最后放置是会调用 onDrop() 方法,在此处进行处理,screenId 为 0 则禁止放置,让图标回位到原始位置

//静止增加 icon
 public void onDrop(final DragObject d) {
        mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
        CellLayout dropTargetLayout = mDropToLayout;

		....

		//cczheng add screenId > 0 for first screen forbidden add icon
        if (screenId > 0 && foundCell) {
            if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
                snapScreen = getPageIndexForScreenId(screenId);
                snapToPage(snapScreen);
            }

        } else {//cczheng add screenId == 0 for first screen forbidden add icon
            if (screenId == 0 || !returnToOriginalCellToPreventShuffling) {
                onNoCellFound(dropTargetLayout);
            }

            // If we can't find a drop location, we return the item to its original position
            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
            mTargetCell[0] = lp.cellX;
            mTargetCell[1] = lp.cellY;
            CellLayout layout = (CellLayout) cell.getParent().getParent();
            layout.markCellsAsOccupiedForView(cell);
        }

		.....

}

//禁止增加 weight
 public boolean acceptDrop(DragObject d) {
        // If it's an external drop (e.g. from All Apps), check if it should be accepted
        CellLayout dropTargetLayout = mDropToLayout;
        if (d.dragSource != this) {
            // Don't accept the drop if we're not over a screen at time of drop
            if (dropTargetLayout == null) {
                return false;
            }
			....
		
			//cczheng add screenId > 0 for first screen forbidden add weight
			long screenId = getIdForScreen(dropTargetLayout);
            // Don't accept the drop if there's no room for the item
            if (screenId==0 || !foundCell) {
                Log.e("Launcher3", "acceptDrop() onNoCellFound");
                onNoCellFound(dropTargetLayout);
                return false;
            }
        }

        long screenId = getIdForScreen(dropTargetLayout);
        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
            commitExtraEmptyScreen();
        }

        return true;
}

Workspace 第一屏图标禁止拖动

vendor\mediatek\proprietary\packages\apps\LauncherSip\src\com\android\launcher3\Launcher.java

在 onLongClick() 中进行 screenId==0 判断

@Override
    public boolean onLongClick(View v) {
        if (!isDraggingEnabled()) return false;
        if (isWorkspaceLocked()) return false;
        if (mState != State.WORKSPACE) return false;

		....

		CellLayout.CellInfo longClickCellInfo = null;
        View itemUnderLongClick = null;
        if (v.getTag() instanceof ItemInfo) {
            ItemInfo info = (ItemInfo) v.getTag();
            longClickCellInfo = new CellLayout.CellInfo(v, info);
            itemUnderLongClick = longClickCellInfo.cell;
            mPendingRequestArgs = null;
        }

         //cczheng add , hotseat can't be moved and deleted [S] 
        if(longClickCellInfo != null && (longClickCellInfo.container==-101
            || longClickCellInfo.screenId==0)){
                    return false;
        }//E
		
		...
}

你可能感兴趣的:(android 8.1 Launcher3 去掉抽屉式,显示所有 app)