android 9.0 Launcher3 去掉抽屉式,显示所有 app
效果图
修改思路
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 中增加权限
<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 点击
/** * 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
<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); 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(iteminfo item) { if (!textutils.isempty(mtext)) { mtext = getresources().getstring(item.id != iteminfo.no_id ? 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(item) ? r.string.remove_drop_target_label : android.r.string.cancel); } requestlayout(); } } private void setcontroltypebasedondragsource(iteminfo item) { mcontroltype = item.id != iteminfo.no_id ? controltype.remove_target : controltype.cancel_target; //add for hide deletedroptarget [s] if (launcherappstate.isdisableallapps()) { mcontroltype = iscandrop(item) ? controltype.remove_target : controltype.cancel_target; } } public void onaccessibilitydrop(view view, iteminfo item) { // 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)) { mlauncher.removeitem(view, item, true /* deletefromdb */); mlauncher.getworkspace().stripemptyscreens(); mlauncher.getdraglayer() .announceforaccessibility(getcontext().getstring(r.string.item_removed)); } } private 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\quickstep\src\com\android\launcher3\uioverrides\overviewtoallappstouchcontroller.java
canintercepttouch() 中增加判断是否直接拦截
@override protected boolean canintercepttouch(motionevent ev) { //add for forbidden workspace drag change gradientview alph if (launcherappstate.isdisableallapps()){ android.util.log.e("launcher3", "canintercepttouch()"); return false; } if (mcurrentanimation != null) { // if we are already animating from a previous state, we can intercept. return true; } if (abstractfloatingview.gettopopenview(mlauncher) != null) { return false; } if (mlauncher.isinstate(all_apps)) { // in all-apps only listen if the container cannot scroll itself return mlauncher.getappsview().shouldcontainerscroll(ev); } else if (mlauncher.isinstate(normal)) { return true; } else if (mlauncher.isinstate(overview)) { recentsview rv = mlauncher.getoverviewpanel(); return ev.gety() > (rv.getbottom() - rv.getpaddingbottom()); } else { return false; } }
8、修改页面指示线为圆点
vendor\mediatek\proprietary\packages\apps\launcher3\res\layout\launcher.xml
workspacepageindicator 改为 pageindicatordots
<com.android.launcher3.pageindicators.pageindicatordots android:id="@+id/page_indicator" android:layout_width="match_parent" android:layout_height="4dp" android:layout_gravity="bottom|center_horizontal" android:theme="@style/homescreenelementtheme" />
vendor\mediatek\proprietary\packages\apps\launcher3\src\com\android\launcher3\pageindicators\pageindicatordots.java
增加 pageindicatordots 继承 insettable,复写setinsets(), 调整圆点的位置
public class pageindicatordots extends view implements pageindicator, insettable { // add for change workspacepageindicator line to dot @override public void setinsets(rect insets) { deviceprofile grid = mlauncher.getdeviceprofile(); framelayout.layoutparams lp = (framelayout.layoutparams) getlayoutparams(); if (grid.isverticalbarlayout()) { rect padding = grid.workspacepadding; lp.leftmargin = padding.left + grid.workspacecellpaddingxpx; lp.rightmargin = padding.right + grid.workspacecellpaddingxpx; lp.bottommargin = padding.bottom; } else { lp.leftmargin = lp.rightmargin = 0; lp.gravity = gravity.center_horizontal | gravity.bottom; lp.bottommargin = grid.hotseatbarsizepx + insets.bottom; } setlayoutparams(lp); } @override public void setscroll(int currentscroll, int totalscroll) { if (mnumpages > 1) { if (misrtl) { currentscroll = totalscroll - currentscroll; } int scrollperpage = totalscroll / (mnumpages - 1); // add for change workspacepageindicator line to dot if (scrollperpage == 0) { return; } int pagetoleft = currentscroll / scrollperpage; int pagetoleftscroll = pagetoleft * scrollperpage; int pagetorightscroll = pagetoleftscroll + scrollperpage; ... }
vendor\mediatek\proprietary\packages\apps\launcher3\src\com\android\launcher3\states\springloadedstate.java
注释 setshouldautohide(),避免长按 workspace 时发生崩溃
@override public void onstateenabled(launcher launcher) { workspace ws = launcher.getworkspace(); ws.showpageindicatoratcurrentscroll(); //annotaion for workspacepageindicator line to dot // ws.getpageindicator().setshouldautohide(false); // prevent any un/installshortcutreceivers from updating the db while we are // in spring loaded mode installshortcutreceiver.enableinstallqueue(installshortcutreceiver.flag_drag_and_drop); launcher.getrotationhelper().setcurrentstaterequest(request_lock); } @override public void onstatedisabled(final launcher launcher) { //annotaion for workspacepageindicator line to dot // launcher.getworkspace().getpageindicator().setshouldautohide(true); // re-enable any un/installshortcutreceiver and now process any queued items installshortcutreceiver.disableandflushinstallqueue( installshortcutreceiver.flag_drag_and_drop, launcher); }
欢迎关注我的英文公众号【十句英文】,每日1首英文金曲+10句英文,伴你共同进步。
微信扫一扫下方二维码即可关注: