1.通常我们遇到的第一个改造点就是去掉原生Launcher自带的底部”全部“图标以及上拉显示的全部APP列表样式,这个不太符合如今国人的操作习惯,而在实际的开发中,发现其涉及到的改造点有如下几个:
去掉上拉展示列表
去掉导航条下方的上拉箭头
确保新安装的APP直接加载到桌面
确保系统APP直接显示到桌面
确保初次启动时即加载全部APP而不是需要安装任一APP时才触发
- 涉及到的类及接口等 去掉上拉展示列表(方法不唯一,暂用最简单方式)
com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController
去掉导航栏下方箭头
com.android.launcher3.view.ScrimView
确保新安装的APP直接加载到桌面
com.android.launcher3.model.LoaderTask com.android.launcher3.model.PackageUpdatedTask com.android.launcher3.InstallShortcutReceiver
确保系统APP直接显示到桌面
com.android.launcher3.model.AddWorkspaceItemsTask
确保初次启动时即加载全部APP而不是需要安装任一APP时才触发
com.android.launcher3.model.BaseModelUpdateTask
- 更改代码
去掉上拉展示列表
这个改动其实有更稳妥的方式,但如果你暂时没有上拉触发个性化交互的其它需求,可采用以下方式。(这里补充说明下,如果是横屏,可能需要改动OverviewToAllAppsTouchController,修改点相同)
PortraitStatesTouchController.java ` @Override protected boolean canInterceptTouch(MotionEvent ev) { //移除竖向抽屉====start==== if(true){ return false; } //移除竖向抽屉====end==== if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { mCurrentAnimation.getAnimationPlayer().end(); }
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
// If we are already animating from a previous state, we can intercept as long as
// the touch is below the current all apps progress (to allow for double swipe).
return true;
}
// Otherwise, make sure everything is settled and don't intercept so they can scroll
// recents, dismiss a task, etc.
if (mAtomicAnim != null) {
mAtomicAnim.end();
}
return false;
}
if (mLauncher.isInState(ALL_APPS)) {
// In all-apps only listen if the container cannot scroll itself
if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
return false;
}
} else if (mLauncher.isInState(OVERVIEW)) {
if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {
return false;
}
} else {
// If we are swiping to all apps instead of overview, allow it from anywhere.
boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
// For all other states, only listen if the event originated below the hotseat height
if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
return false;
}
}
if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
return false;
}
return true;
}
`
去掉导航条下方的上拉箭头 在某些设备上可能受屏幕宽高比或其它影响,这个箭头可能不显示,但考虑兼容性还是建议加上处理,这里改完之后,调试时可能会发现有一个不影响运行的报错”ShortcutRequest: Failed to query for shortcuts“,源于缺失ensureShortcutPermission,当为默认启动器时,推断无此报错,可暂不处理。 ———————————————— ScrimView.java
private void updateDragHandleVisibility(@Nullable Drawable recycle) {
boolean visible = shouldDragHandleBeVisible();
boolean wasVisible = mDragHandle != null;
if (visible != wasVisible) {
if (visible) {
//隐藏上拉箭头,这里取最小改动,永不展示该箭头====改动start====
mDragHandle = recycle != null ? recycle :
mLauncher.getDrawable(0);//R.drawable.drag_handle_indicator_shadow);
//====改动end====
mDragHandle.setBounds(mDragHandleBounds);
updateDragHandleAlpha();
} else {
mDragHandle = null;
}
invalidate();
}
}
确保新安装的APP直接加载到桌面
这里涉及到三个类的改动LoaderTask.java、PackageUpdatedTask.java、InstallShortReceiver.java。
InstallShortReceiver.java,这个只是需要将静态内部类PendingInstallShortcutInfo的可见性改为public。
LoaderTask.java `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)) {
//...//
// fifth step
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
loadFolderNames();
}
verifyNotStopped();
updateHandler.finish();
logger.addSplit("finish icon update");
//预制应用展示在Workspace区====改动start==== verifyApplications(); //====改动end====
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
logger.addSplit("Cancelled");
} finally {
logger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
//====增加方法==== private void verifyApplications(){ final Context context = mApp.getContext(); ArrayList<Pair<ItemInfo,Object>> installQueue = new ArrayList<>(); final List profiles = mUserManager.getUserProfiles(); for(UserHandle user : profiles){ final List apps = mLauncherApps.getActivityList(null,user); ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> 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); } } } `
PackageUpdatedTask.java
@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<>();
//...//
bindApplicationsIfNeeded();
//====改动start====
updateToWorkSpace(context,app,appsList);
//====改动end====
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
//...//
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);
}
}
//添加方法===改动====
//安装的app添加到workspace工作区
public void updateToWorkSpace(Context context,LauncherAppState app,AllAppsList appsList){
ArrayList<Pair<ItemInfo,Object>> installQueue = new ArrayList<>();
UserManager mUserManager = app.getContext().getSystemService(UserManager.class);
final List<UserHandle> profiles = mUserManager.getUserProfiles();
ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<>();
LauncherApps mLauncherApps = app.getContext().getSystemService(LauncherApps.class);
for(UserHandle user : profiles){
final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null,user);
synchronized (this){
for(LauncherActivityInfo info :apps){
for(AppInfo appInfo:appsList.data){
String packageName = info.getComponentName().getPackageName();
if(info.getComponentName().equals(appInfo.componentName)){
if(DEBUG){
Log.d(TAG,"updateToWorkSpace packageName: " + packageName);
}
InstallShortcutReceiver.PendingInstallShortcutInfo mPendingInstallShortcutInfo
= new InstallShortcutReceiver.PendingInstallShortcutInfo(info,context);
added.add(mPendingInstallShortcutInfo);
installQueue.add(mPendingInstallShortcutInfo.getItemInfo());
}
}
}
}
}
if(!added.isEmpty()){
app.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
}
private WorkspaceItemInfo createWorkSpaceItemInfo(Intent data,UserHandle user,LauncherAppState app){
if(data == null){
Log.e(TAG,"Can't construct WorkspaceItemInfo with null data");
return null;
}
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
if(null == intent){
//If the intent is null ,return null as we can't construct a valid WorkspaceItemInfo
return null;
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
info.user = user;
BitmapInfo iconInfo = null;
LauncherIcons li = LauncherIcons.obtain(app.getContext());
if(bitmap instanceof Bitmap){
iconInfo = li.createIconBitmap((Bitmap) bitmap);
}else{
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if(extra instanceof Intent.ShortcutIconResource){
info.iconResource = (Intent.ShortcutIconResource)extra;
iconInfo = li.createIconBitmap(info.iconResource);
}
}
li.recycle();
if(null == iconInfo){
iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
info.bitmap = iconInfo;
info.title = Utilities.trim(name);
info.contentDescription = app.getContext().getPackageManager().getUserBadgedLabel(info.title,info.user);
info.intent = intent;
return info;
}
确保系统APP直接显示到桌面
经过上面的改动之后,运行发现预装的APP和安装的APP都可以显示,但是系统自带的APP如相册、通讯录等不见了,这里是因为原代码中将该部分APP过滤了,需要去掉
AddWorkspaceItemsTask.java `@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;
Log.e("item=====",item.toString());
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
//去掉对系统app的忽略处理====改动start==== // if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) { // continue; // } //====改动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);
//...//
}
}
`
确保初次启动时即加载全部APP而不是需要安装任一APP时才触发
在完成以上处理之后会发现,运行完毕后并不能将所有APP铺展到桌面上,但执行任一APP的安装过程之后就会触发了,这里需要作一下处理
BaseModelUpdateTask.java
@Override public final void run() { //注掉这个判断,就能让初次启动就加载所有到workspace if (!mModel.isModelLoaded()) { if (DEBUG_TASKS) { Log.d(TAG, "Ignoring model task since loader is pending=" + this); } // Loader has not yet run. //====改动start==== //或者这里简单处理,注掉return,能让初次启动就加载所有到workspace,否则AddWorkspaceItemsTask的execute未执行 //return; //====改动end==== } execute(mApp, mDataModel, mAllAppsList); }