Android11 Launcher3去掉抽屉改为单层
1.前言:
之前在Android12 Rom定制的时候实现过不去掉抽屉显示所有应用列表,今天来讲解下在Android11时实现去掉抽屉显示在桌面所有应用列表,这里Android9和11的系统源码都是不一样的,所以改动有所区别,本文先讲解Android11的修改方式.
2.修改的核心类如下:
AllAppsTransitionController
FeatureFlags
DragController
AddWorkspaceItemsTask
LoaderCursor
LoaderTask
PackageUpdatedTask
AbstractStateChangeTouchController
DeleteDropTarget
DeviceProfile
Hotseat
InvariantDeviceProfile
3.修改AllAppsTransitionController
源码路径:packages/apps/launcher3/src/com/android/launcher3/allapps/AllAppsTransitionController.java
核心修改如下:
csharp
public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
mAppsView = appsView;
mScrimView = scrimView;
// add start
if (FeatureFlags.REMOVE_DRAWER) {
mScrimView.setVisibility(View.GONE);
}
// add end
PluginManagerWrapper.INSTANCE.get(mLauncher)
.addPluginListener(this, AllAppsSearchPlugin.class, false);
}
4.修改FeatureFlags:
源码路径:packages/apps/launcher3/src/com/android/launcher3/config/FeatureFlags.java
作用:各种功能常量工具类
核心修改如下:
arduino
添加一个变量控制是否显示抽屉
/**
* true: All applications are displayed in the workspace. Turn off the display of the allapp list
*/
public static final boolean REMOVE_DRAWER = true;
5.修改DragController:
源码路径:packages/apps/launcher3/src/com/android/launcher3/dragndrop/DragController.java
作用:控制桌面图标是否可以拖拽。
核心修改如下:
在DragController的drop方法中添加桌面图标是否取消拖拽
ini
private void drop(DropTarget dropTarget, Runnable flingAnimation) {
final int[] coordinates = mCoordinatesTemp;
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
// Move dragging to the final target.
if (dropTarget != mLastDropTarget) {
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragObject);
}
mLastDropTarget = dropTarget;
if (dropTarget != null) {
dropTarget.onDragEnter(mDragObject);
}
}
mDragObject.dragComplete = true;
if (mIsInPreDrag) {
if (dropTarget != null) {
dropTarget.onDragExit(mDragObject);
}
return;
}
// Drop onto the target.
boolean accepted = false;
if (dropTarget != null) {
dropTarget.onDragExit(mDragObject);
if (dropTarget.acceptDrop(mDragObject)) {
if (flingAnimation != null) {
flingAnimation.run();
} else {
dropTarget.onDrop(mDragObject, mOptions);
}
// add start
if (FeatureFlags.REMOVE_DRAWER) {
if (dropTarget instanceof DeleteDropTarget && canCancel(mDragObject.dragInfo)) {
cancelDrag();
}
}
// add end
accepted = true;
}
}
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
dispatchDropComplete(dropTargetAsView, accepted);
}
// add start
private boolean canCancel(ItemInfo item){
return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
}
//add end
6.修改AddWorkspaceItemsTask:
源码路径:packages/apps/launcher3/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
作用:添加App图标到桌面
核心修改如下:(不过滤系统和飞系统图标,显示所有的图标)
scss
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
if (mItemList.isEmpty()) {
return;
}
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final IntArray addedWorkspaceScreensFinal = new IntArray();
synchronized(dataModel) {
IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
List<ItemInfo> filteredItems = new ArrayList<>();
for (Pair<ItemInfo, Object> entry : mItemList) {
ItemInfo item = entry.first;
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
continue;
}
// b/139663018 Short-circuit this logic if the icon is a system app
// add start
if (!FeatureFlags.REMOVE_DRAWER) {
// b/139663018 Short-circuit this logic if the icon is a system app
if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) {
continue;
}
}
// add end
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
if (item instanceof AppInfo) {
item = ((AppInfo) item).makeWorkspaceItem();
}
}
if (item != null) {
filteredItems.add(item);
}
}
InstallSessionHelper packageInstaller =
InstallSessionHelper.INSTANCE.get(app.getContext());
LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class);
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
addedWorkspaceScreensFinal, item.spanX, item.spanY);
int screenId = coords[0];
ItemInfo itemInfo;
if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo ||
item instanceof LauncherAppWidgetInfo) {
itemInfo = item;
} else if (item instanceof AppInfo) {
itemInfo = ((AppInfo) item).makeWorkspaceItem();
} else {
throw new RuntimeException("Unexpected info type");
}
if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
String packageName = item.getTargetComponent() != null
? item.getTargetComponent().getPackageName() : null;
if (packageName == null) {
continue;
}
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
packageName);
List<LauncherActivityInfo> activities = launcherApps
.getActivityList(packageName, item.user);
boolean hasActivity = activities != null && !activities.isEmpty();
if (sessionInfo == null) {
if (!hasActivity) {
// Session was cancelled, do not add.
continue;
}
} else {
workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
}
if (hasActivity) {
// App was installed while launcher was in the background,
// or app was already installed for another user.
itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
.makeWorkspaceItem();
if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) {
// We need this additional check here since we treat all auto added
// workspace items as promise icons. At this point we now have the
// correct intent to compare against existing workspace icons.
// Icon already exists on the workspace and should not be auto-added.
continue;
}
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
app.getIconCache().getTitleAndIcon(wii,
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
}
}
// Add the shortcut to the db
getModelWriter().addItemToDatabase(itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
coords[1], coords[2]);
// Save the WorkspaceItemInfo for binding in the workspace
addedItemsFinal.add(itemInfo);
}
}
if (!addedItemsFinal.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
int lastScreenId = info.screenId;
for (ItemInfo i : addedItemsFinal) {
if (i.screenId == lastScreenId) {
addAnimated.add(i);
} else {
addNotAnimated.add(i);
}
}
}
callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
addNotAnimated, addAnimated);
}
});
}
}
7.修改LoaderCursor:
源码路径:packages/apps/launcher3/src/com/android/launcher3/model/LoaderCursor.java
作用:
核心修改如下:
kotlin
/**
* check & update map of what's occupied; used to discard overlapping/invalid items
*/
protected boolean checkItemPlacement(ItemInfo item) {
int containerIndex = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
// add start
if (FeatureFlags.REMOVE_DRAWER) {
return false;
}
// add end
final GridOccupancy hotseatOccupancy =
occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT);
if (item.screenId >= mIDP.numHotseatIcons) {
Log.e(TAG, "Error loading shortcut " + item
+ " into hotseat position " + item.screenId
+ ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+ ")");
return false;
}
if (hotseatOccupancy != null) {
if (hotseatOccupancy.cells[(int) item.screenId][0]) {
Log.e(TAG, "Error loading shortcut into hotseat " + item
+ " into position (" + item.screenId + ":" + item.cellX + ","
+ item.cellY + ") already occupied");
return false;
} else {
hotseatOccupancy.cells[item.screenId][0] = true;
return true;
}
} else {
final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
occupancy.cells[item.screenId][0] = true;
occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
return true;
}
} else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
// Skip further checking if it is not the hotseat or workspace container
return true;
}
final int countX = mIDP.numColumns;
final int countY = mIDP.numRows;
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
item.cellX < 0 || item.cellY < 0 ||
item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
Log.e(TAG, "Error loading shortcut " + item
+ " into cell (" + containerIndex + "-" + item.screenId + ":"
+ item.cellX + "," + item.cellY
+ ") out of screen bounds ( " + countX + "x" + countY + ")");
return false;
}
if (!occupied.containsKey(item.screenId)) {
GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
if (item.screenId == Workspace.FIRST_SCREEN_ID) {
// Mark the first row as occupied (if the feature is enabled)
// in order to account for the QSB.
screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
}
occupied.put(item.screenId, screen);
}
final GridOccupancy occupancy = occupied.get(item.screenId);
// Check if any workspace icons overlap with each other
if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
occupancy.markCells(item, true);
return true;
} else {
Log.e(TAG, "Error loading shortcut " + item
+ " into cell (" + containerIndex + "-" + item.screenId + ":"
+ item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
+ ") already occupied");
return false;
}
}
8.修改LoaderTask:
源码路径:packages/apps/launcher3/src/com/android/launcher3/model/LoaderTask.java
作用:加载桌面图标的线程
核心修改如下:(在加载桌面图标时如果是显示所有应用就不过滤图标,使用bindAllAppsToWorkspace方法)
scss
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");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
loadCachedPredictions();
logger.addSplit("loadWorkspace");
verifyNotStopped();
mResults.bindWorkspace();
logger.addSplit("bindWorkspace");
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
// Take a break
waitForIdle();
logger.addSplit("step 1 complete");
verifyNotStopped();
// second step
List<LauncherActivityInfo> allActivityList = loadAllApps();
logger.addSplit("loadAllApps");
// add start
if (FeatureFlags.REMOVE_DRAWER) {
bindAllAppsToWorkspace();
}
// add end
verifyNotStopped();
mResults.bindAllApps();
logger.addSplit("bindAllApps");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
logger.addSplit("update icon cache");
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
verifyNotStopped();
logger.addSplit("save shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
}
// Take a break
waitForIdle();
logger.addSplit("step 2 complete");
verifyNotStopped();
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
logger.addSplit("loadDeepShortcuts");
verifyNotStopped();
mResults.bindDeepShortcuts();
logger.addSplit("bindDeepShortcuts");
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
verifyNotStopped();
logger.addSplit("save deep shortcuts in icon cache");
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
}
// Take a break
waitForIdle();
logger.addSplit("step 3 complete");
verifyNotStopped();
// fourth step
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
logger.addSplit("load widgets");
verifyNotStopped();
mResults.bindWidgets();
logger.addSplit("bindWidgets");
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
logger.addSplit("save widgets in icon cache");
// fifth step
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
loadFolderNames();
}
verifyNotStopped();
updateHandler.finish();
logger.addSplit("finish icon update");
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
logger.addSplit("Cancelled");
} finally {
logger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
// add start
private void bindAllAppsToWorkspace(){
if (mBgAllAppsList.data.size() > 0) {
AppInfoComparator mAppNameComparator = new AppInfoComparator(mApp.getContext());
ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>(mBgAllAppsList.data);
// 按照名称进行排序
Collections.sort(appInfos, mAppNameComparator);
ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
for (AppInfo info : appInfos) {
installQueue.add(Pair.create((ItemInfo) info, null));
}
mApp.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
}
// end
9.修改PackageUpdatedTask:
源码路径:packages/apps/launcher3/src/com/android/launcher3/model/PackageUpdatedTask.java
作用:在桌面App图标发生变化时的线程
核心修改如下:(如果显示所有图标时,在线程的execute方法中添加加载所有图标方法)
ini
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
final String[] packages = mPackages;
final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>();
switch (mOp) {
case OP_ADD: {
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
String pkg = packages[i];
if ("com.android.calendar".equals(pkg)
|| "com.android.deskclock".equals(pkg)
|| "com.android.dialer".equals(pkg)
|| "com.android.browser".equals(pkg)
|| "com.android.contacts".equals(pkg)
|| "com.android.camera2".equals(pkg)
|| "com.android.email".equals(pkg)
|| "com.android.calculator2".equals(pkg)
|| "com.android.webview".equals(pkg)
|| "com.android.gallery3d".equals(pkg)
|| "com.android.music".equals(pkg)) {
continue;
}
iconCache.updateIconsForPkg(packages[i], mUser);
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
appsList.addPackage(context, packages[i], mUser);
// Automatically add homescreen icon for work profile apps for below O device.
if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
}
}
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
}
case OP_UPDATE:
try (SafeCloseable t =
appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
String pkg = packages[i];
if ("com.android.calendar".equals(pkg)
|| "com.android.deskclock".equals(pkg)
|| "com.android.dialer".equals(pkg)
|| "com.android.browser".equals(pkg)
|| "com.android.contacts".equals(pkg)
|| "com.android.camera2".equals(pkg)
|| "com.android.email".equals(pkg)
|| "com.android.calculator2".equals(pkg)
|| "com.android.webview".equals(pkg)
|| "com.android.gallery3d".equals(pkg)
|| "com.android.music".equals(pkg)) {
continue;
}
iconCache.updateIconsForPkg(packages[i], mUser);
appsList.updatePackage(context, packages[i], mUser);
app.getWidgetCache().removePackage(packages[i], mUser);
}
}
// Since package was just updated, the target must be available now.
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_REMOVE: {
for (int i = 0; i < N; i++) {
FileLog.d(TAG, "Removing app icon" + packages[i]);
iconCache.removeIconsForPkg(packages[i], mUser);
}
// Fall through
}
case OP_UNAVAILABLE:
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
appsList.removePackage(packages[i], mUser);
app.getWidgetCache().removePackage(packages[i], mUser);
}
flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_SUSPEND:
case OP_UNSUSPEND:
flagOp = mOp == OP_SUSPEND ?
FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) :
FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
appsList.updateDisabledFlags(matcher, flagOp);
break;
case OP_USER_AVAILABILITY_CHANGE: {
UserManagerState ums = new UserManagerState();
ums.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
}
}
bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
// For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
boolean infoUpdated = false;
boolean shortcutUpdated = false;
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
LauncherIcons li = LauncherIcons.obtain(context);
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
si.bitmap = iconInfo;
infoUpdated = true;
}
}
ComponentName cn = si.getTargetComponent();
if (cn != null && matcher.matches(si, cn)) {
String packageName = cn.getPackageName();
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
removedShortcuts.put(si.id, false);
if (mOp == OP_REMOVE) {
continue;
}
}
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
si.getDeepShortcutId())
.query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
} else {
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class)
.isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.put(si.id, true);
continue;
}
} else if (!isTargetValid) {
removedShortcuts.put(si.id, true);
FileLog.e(TAG, "Restored shortcut no longer valid "
+ si.getIntent());
continue;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
infoUpdated = true;
}
} else if (isNewApkAvailable && removedComponents.contains(cn)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
}
}
if (isNewApkAvailable &&
si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
int oldRuntimeFlags = si.runtimeStatusFlags;
si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
}
}
if (infoUpdated || shortcutUpdated) {
updatedWorkspaceItems.add(si);
}
if (infoUpdated) {
getModelWriter().updateItemInDatabase(si);
}
} else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup'
// if the widget has a config activity. In case there is no config
// activity, it will be marked as 'restored' during bind.
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
}
}
}
}
bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
}
if (!widgets.isEmpty()) {
scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets));
}
}
final HashSet<String> removedPackages = new HashSet<>();
if (mOp == OP_REMOVE) {
// Mark all packages in the broadcast to be removed
Collections.addAll(removedPackages, packages);
// No need to update the removedComponents as
// removedPackages is a super-set of removedComponents
} else if (mOp == OP_UPDATE) {
// Mark disabled packages in the broadcast to be removed
final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
for (int i=0; i<N; i++) {
if (!launcherApps.isPackageEnabled(packages[i], mUser)) {
removedPackages.add(packages[i]);
}
}
}
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
.and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
}
if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < N; i++) {
dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
}
bindUpdatedWidgets(dataModel);
}
// add start
if (FeatureFlags.REMOVE_DRAWER) {
bindAllAppsToWorkspace(app, appsList);
}
// add end
}
// add start
private void bindAllAppsToWorkspace(LauncherAppState app, AllAppsList mBgAllAppsList){
if (mBgAllAppsList.data.size() > 0) {
// AppInfoComparator mAppNameComparator = new AppInfoComparator(mApp.getContext());
ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>(mBgAllAppsList.data);
// Collections.sort(appInfos, mAppNameComparator);
ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
for (AppInfo info : appInfos) {
installQueue.add(Pair.create((ItemInfo) info, null));
}
app.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
}
// add end
10.修改AbstractStateChangeTouchController:
源码路径:packages/apps/launcher3/src/com/android/launcher3/touch/AbstractStateChangeTouchController
作用:控制桌面图标的触摸事件包含拖拽、删除、更新等
核心修改如下:(如果是单层时取消拖拽)
ini
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = !canInterceptTouch(ev);
if (mNoIntercept) {
return false;
}
// Now figure out which direction scroll events the controller will start
// calling the callbacks.
final int directionsToDetectScroll;
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
} else {
directionsToDetectScroll = getSwipeDirection();
if (directionsToDetectScroll == 0) {
mNoIntercept = true;
return false;
}
}
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
}
// add start
if (FeatureFlags.REMOVE_DRAWER) {
return false;
}
// add end
if (mNoIntercept) {
return false;
}
onControllerTouchEvent(ev);
return mDetector.isDraggingOrSettling();
}
11.修改DeleteDropTarget:
源码路径:packages/apps/launcher3/src/com/android/launcher3/DeleteDropTarget.java
作用:桌面图标的删除事件处理
核心修改如下:(在单层时控制图标是否取消拖拽)
arduino
private boolean canRemove(ItemInfo item) {
// ad start
boolean remove = FeatureFlags.REMOVE_DRAWER ? !canCancel(item)
: item.id != ItemInfo.NO_ID;
// add end
return remove;
}
/**
* Set mControlType depending on the drag item.
*/
private void setControlTypeBasedOnDragSource(ItemInfo item) {
mControlType = (FeatureFlags.REMOVE_DRAWER ? !canCancel(item) :
item.id != ItemInfo.NO_ID) ? ControlType.REMOVE_TARGET
: ControlType.CANCEL_TARGET;
}
// add start
private boolean canCancel(ItemInfo item){
return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
}
// add end
12.修改DeviceProfile:
源码路径:packages/apps/launcher3/src/com/android/launcher3/DeviceProfile.java
作用:控制桌面图标的间距
核心修改如下:
ini
/**
* Updates {@link #workspacePadding} as a result of any internal value change to reflect the
* new workspace padding
*/
private void updateWorkspacePadding() {
Rect padding = workspacePadding;
if (isVerticalBarLayout()) {
padding.top = 0;
padding.bottom = edgeMarginPx;
if (isSeascape()) {
padding.left = hotseatBarSizePx;
padding.right = hotseatBarSidePaddingStartPx;
} else {
padding.left = hotseatBarSidePaddingStartPx;
padding.right = hotseatBarSizePx;
}
} else {
// add start
if (FeatureFlags.REMOVE_DRAWER) {
hotseatBarSizePx = 0;
}
// add end
int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight
- mWorkspacePageIndicatorOverlapWorkspace;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
// The amount of screen space available for left/right padding.
int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) +
((inv.numColumns - 1) * cellWidthPx)));
availablePaddingX = (int) Math.min(availablePaddingX,
widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
- (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
- hotseatBarBottomPaddingPx);
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes
padding.set(desiredWorkspaceLeftRightMarginPx,
edgeMarginPx,
desiredWorkspaceLeftRightMarginPx,
paddingBottom);
}
}
}
13.修改Hotseat:
源码路径:packages/apps/launcher3/src/com/android/launcher3/Hotseat.java
作用:设置桌面图标显示的位置和宽高
核心修改如下:
ini
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mActivity.getDeviceProfile();
if (grid.isVerticalBarLayout()) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
if (grid.isSeascape()) {
lp.gravity = Gravity.LEFT;
lp.width = grid.hotseatBarSizePx + insets.left;
} else {
lp.gravity = Gravity.RIGHT;
lp.width = grid.hotseatBarSizePx + insets.right;
}
} else {
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = grid.hotseatBarSizePx + insets.bottom;
}
Rect padding = grid.getHotseatLayoutPadding();
setPadding(padding.left, padding.top, padding.right, padding.bottom);
// add start
if (FeatureFlags.REMOVE_DRAWER) {
lp.height = 0;
}
// add end
setLayoutParams(lp);
InsettableFrameLayout.dispatchInsets(this, insets);
}
14.修改InvariantDeviceProfile:
源码路径:packages/apps/launcher3/src/com/android/launcher3/InvariantDeviceProfile
作用:控制桌面图标的排列样式
核心修改如下:(这里在单层时修改为5层行5列,真正的Launcher可以自定义为其他行数)
ini
private void initGrid(
Context context, DefaultDisplay.Info displayInfo, DisplayOption displayOption) {
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
// add start
if (FeatureFlags.REMOVE_DRAWER) {
numRows = 5;
numColumns = 5;
}
// add end
numHotseatIcons = closestProfile.numHotseatIcons;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
numAllAppsColumns = closestProfile.numAllAppsColumns;
mExtraAttrs = closestProfile.extraAttrs;
iconSize = displayOption.iconSize;
iconShapePath = getIconShapePath(context);
landscapeIconSize = displayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
iconTextSize = displayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
if (Utilities.isGridOptionsEnabled(context)) {
allAppsIconSize = displayOption.allAppsIconSize;
allAppsIconTextSize = displayOption.allAppsIconTextSize;
} else {
allAppsIconSize = iconSize;
allAppsIconTextSize = iconTextSize;
}
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
Point realSize = new Point(displayInfo.realSize);
// The real size never changes. smallSide and largeSide will remain the
// same in any orientation.
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo)
.setSizeRange(new Point(displayInfo.smallestSize),
new Point(displayInfo.largestSize));
landscapeProfile = builder.setSize(largeSide, smallSide).build();
portraitProfile = builder.setSize(smallSide, largeSide).build();
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
defaultWallpaperSize = new Point(
(int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
largeSide);
} else {
defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
}
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
}
15.实现效果如下:

16.总结:
以上就是今天的内容修改Android11 launcher3桌面图标的样式去掉抽屉显示所有应用列表,这其中遇到很多问题,但是最后都通过仔细排查和日志解决了,源码修改和编译其实还是很有意思的,排查起来比App开发要复杂,编译的时候很久,所以需要阅读大量源码,有时候源码太多,不一定要看所有的,只要抓住关键点去看,搞明白其原理和流程,修改起来就会很顺利.
- 没有导包导致编译失败,这里由于源码太多没有给出所有源码,大家可以下载源码导包后自行编译.
- 没有去掉过滤发发发导致系统图标没有显示
- 没有在更新图标时加载所有图标导致新安装的系统应用没有显示
- 没有去掉重复的图标和更新时的拖拽
- 在数据库图标更新时发生崩溃异常,需要根据具体日志排查解决
- 在编译源码的时候一定要仔细阅读,小心添加逻辑,加上注释避免后面问题排查困难.