android launcher 之踩到的坑
需求:
1、 用android系统launcher 隐藏主菜单 所有应用显示在桌面 即workspace上;
2、隐藏launcher上方默认的google search;
3、切换一套launcher主题。
实现效果:
分析:
1、 隐藏主菜单 ,google默认在android L 版本有一个隐藏主菜单的开关——LauncherAppState.isDisableAllApps()
返回 true 隐藏,返回 false 显示。
2、隐藏google search ,有好多方法。比较简单 ,本文将一笔带过
3、切换主题 上次有提过实现思路,具体看安装包性能优化,动态加载资源
下面是填坑之路
1、隐藏主菜单
首先设置 LauncherAppState.isDisableAllApps() 隐藏主菜单
具体原理的话
这个将会在Hotseat.java中根据上面条件进行判断是显示还是隐藏 ,Allapp其实就是一个TextView 当然仅仅这样是不够的 更细节的东西有时间的话在深入分析
坑一、通过上面的步骤的话 我们默认是把所有应用显示在了workspace上,然而用系统launcher的人都知道在workspace上默认只有移除动作的如下
只有在主菜单中拖拽才有 应用信息和卸载操作,这个问题将直接导致,如果你想卸载应用只能到设置里面去卸载,在桌面上移除只是remove掉了 而它依然还存在 重启launcher就会发现这个应用又回来了,这就是个比较坑爹的事了,
解决办法:
首先找到顶部显示 uninstall和Appinfo的地方
通过分析最后发现是在ButtonDropTarget两个子类里面显示的
DeleteDropTarget和InfoDropTarget 看名字也知道他们是干什么的了,而ButtonDropTarget又实现了launcher定义的两个接口
public class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener
发现google工程师就是比较牛逼起名字都起的这么好。
DropTarget 监听目标的放置操作
DragController 拖拽事件的控制类
而我们会发现这两个类里面都有一个isVisible 通过分析发现这个就是控制launcher顶部的两个标签是否显示的开关。
在InfoDropTarget里面 我们发现
if (!source.supportsAppInfoDropTarget()) {
isVisible = false;
}
而supportsAppInfoDropTarget 是一个接口的方法,它有三个实现类
只有是AppsCustomizePagedView时才返回true
另外两个都是false 所以我们知道了为什么在主菜单里能显示应用信息而桌面不能显示应用信息的原因了
所以这里把workspace的这个方法也修改为true
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
if (LauncherLog.DEBUG) {
LauncherLog.d(TAG, "onDratStart: source = " + source + ", info = " + info
+ ", dragAction = " + dragAction);
}
boolean isVisible = true;
// Hide this button unless we are dragging something from AllApps
//by lly for disableAllapp 20161112 start
if(info instanceof FolderInfo){
isVisible = false;
}
if(info instanceof LauncherAppWidgetInfo){
isVisible = false;
}
if(info instanceof PendingAddWidgetInfo){
isVisible = false;
}
//by lly for disableAllapp 20161112 end
if (!source.supportsAppInfoDropTarget()) {
isVisible = false;
}
....
}
然后在各个情况判断下 当是文件 或者桌面小部件或者从widgets视图中拖拽时都让它不显示appinfo
至此 InfoDropTarget 处理 就完成了
下面看下DeleteDropTarget的处理
其实和处理InfoDropTarget 类似,只是这里更繁琐一点 下面就直接上代码了
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
boolean isVisible = true;
//by lly for disableAllapp
boolean useUninstallLabel = isShortcut(source,info);//isAllAppsApplication(source, info);
boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
// If we are dragging an application from AppsCustomize, only show the control if we can
// delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
// Hide the delete target if it is a widget from AppsCustomize.
//by lly for disableAllapp start
if(info instanceof ShortcutInfo){
try{
ShortcutInfo appInfo = (ShortcutInfo) info;
PackageManager packageManager = getContext().getPackageManager();
ApplicationInfo ai = packageManager.getApplicationInfo(appInfo.intent.getComponent().getPackageName(), 0);
mIsSysApp = (ai.flags & ApplicationInfo.FLAG_SYSTEM)>0;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
//by lly for disableAllapp end
if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
isVisible = false;
}
//by lly for disallapp 20161112 start
private boolean isShortcut(DragSource source, Object info) {
return source.supportsAppInfoDropTarget() && (info instanceof ShortcutInfo);
}
//by lly for disallapp 20161112 start
private boolean isAllAppsApplication(DragSource source, Object info) {
return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
}
public static boolean willAcceptDrop(Object info) {
if (info instanceof ItemInfo) {
ItemInfo item = (ItemInfo) info;
...
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
item instanceof ShortcutInfo) {
if (LauncherAppState.isDisableAllApps()) {
ShortcutInfo shortcutInfo = (ShortcutInfo) info;
//by lly for disableAllapp start
if(!mIsSysApp){
shortcutInfo.flags = 1;
}
//by lly for disableAllapp end
return (shortcutInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
} else {
return true;
}
}
}
return false;
}
看上面有注释的地方差不多就知道我是怎么做的了,首先我是让上面都显示uninstall
主要就是useUninstallLabel 为true
接着判读是否是系统应用 因为系统应用是不能卸载的
if(info instanceof ShortcutInfo){
try{
ShortcutInfo appInfo = (ShortcutInfo) info;
PackageManager packageManager = getContext().getPackageManager();
ApplicationInfo ai = packageManager.getApplicationInfo(appInfo.intent.getComponent().getPackageName(), 0);
mIsSysApp = (ai.flags & ApplicationInfo.FLAG_SYSTEM)>0;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
根据是否是系统应用来确定 flags
从而控制isVisible的值
至此 显示uninstall已经出来完成
接下来看功能即 拖拽到uninstall标签怎么删除
private void completeDrop(DragObject d) {
...
if (LauncherLog.DEBUG) {
LauncherLog.d(TAG, "completeDrop: item = " + item + ", d = " + d);
}
if (isAllAppsApplication(d.dragSource, item)) {
...
} else if (isUninstallFromDisableAllApp(d)) {//by lly for
...
mWaitingForUninstall = mLauncher.startApplicationUninstallActivity(
componentName, shortcut.flags, user);
}
}
private boolean isUninstallFromWorkspace(DragObject d) {
if (LauncherAppState.isDisableAllApps() && isWorkspaceOrFolderApplication(d)) {
ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo;
// Only allow manifest shortcuts to initiate an un-install.
return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent);
}
return false;
}
//by lly for disableAllapp start
private boolean isUninstallFromDisableAllApp(DragObject d) {
if (d.dragInfo instanceof LauncherAppWidgetInfo) {
return false;
}
return true;
}
//by lly for disableAllapp end
依然是重写判读条件 没什么好说的
到这一步基本功能 就已经实现 但是操作的话发现拖拽到标签上松手 图标消失了 不管是appinfo标签还是uninstall标签
这里直接给出答案 他不是消失之时被隐藏了你拖拽个别的图标那个位置他就又出现了 但这确实是个bug 怎么解呢
/** Indicates that the drag operation was cancelled */
public boolean cancelled = false;
if (componentName != null) {
mLauncher.startApplicationDetailsActivity(componentName, user);
}
d.cancelled = true;//by lly
当显示应用信息的时候直接让cancelled 为true表示取消这个拖拽事件就OK了
在DeleteDropTarget中 把animateToTrashAndCompleteDrop(d)提到acceptDrop里来操作
@Override
public boolean acceptDrop(DragObject d) {
//by lly for disableAllapp start
animateToTrashAndCompleteDrop(d);
if(isUninstallFromDisableAllApp(d)){
d.cancelled = true;
}
return false;//willAcceptDrop(d.dragInfo);
//by lly for disableAllapp end
}
到这里坑一就填平了,测试了一下暂时没有发现这样修改有新坑出现。
坑二
通过google提供的方法隐藏主菜单后 1、按菜单键桌面小部件界面出现不了,2、 当长按桌面空白处进入小部件界面然后返回 界面显示异常,3、拖拽小部件到桌面松开手后显示异常。
这个就不买关子了直接说明原因,至于我为什么知道你没看这都星期几了 我还在解bug么
因为按google这样设置后可以看到
void resetLayout() {
mContent.removeAllViewsInLayout();
if (!LauncherAppState.isDisableAllApps()) {
...
if (mLauncher != null) {
allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
mLauncher.setAllAppsButton(allAppsButton);
allAppsButton.setOnClickListener(mLauncher);
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
...
}
}
这个地方主要就是那个allapp菜单添加的地方但是那个对我们分析来说没什么用 主要是看
mLauncher.setAllAppsButton(allAppsButton);
这里把这个菜单设置给了launcher
/**
* Sets the all apps button. This method is called from {@link Hotseat}.
*/
public void setAllAppsButton(View allAppsButton) {
mAllAppsButton = allAppsButton;
}
public View getAllAppsButton() {
return mAllAppsButton;
}
然后通过getAllAppsButton得到这个mAllAppsButton最后在showAppsCustomizeHelper和hideAppsCustomizeHelper里面用到了这个mAllAppsButton其实是个TextView 然而 我们设置隐藏主菜单后 这个mAllAppsButton是空的所以导致了这个问题,那么知道了原因解决就很好办了 。
// If for some reason our views aren't initialized, don't animate
// boolean initialized = getAllAppsButton() != null;
if (animated /*&& initialized*/) {
...
}
直接不要他了不就可以。 当然下面几个地方也要一并去掉。
至此坑二也填平了
2、隐藏launcher上方默认的google search;
3、切换一套launcher主题。
这两个需求比较简单就不再分析了 如有需要请私信给我。
android launcher 之踩到的坑的更多相关文章
- android开发过程中踩过的坑
1) 4.X下 viewgroup 不一定会向下传递requestLayout,当onlayout的速度比较慢(比如子View比较复杂之类的原因),系统会跳帧!此时子View下层的view可能就不会再 ...
- Android Studio安装踩坑
title: Android Studio安装踩坑 date: 2018-09-07 19:31:32 updated: tags: [Android,Android Studio,坑] descri ...
- 【转载】Fragment 全解析(1):那些年踩过的坑
http://www.jianshu.com/p/d9143a92ad94 Fragment系列文章:1.Fragment全解析系列(一):那些年踩过的坑2.Fragment全解析系列(二):正确的使 ...
- 在Mac osx使用ADT Bundle踩过的坑
前言 本篇博客整理一下笔者在Mac下使用ADT Bundle踩过的坑,Google现在也不支持Eclipse了,开发者也到了抛弃Eclipse的时候,但考虑到大部分Java的开发者还是比较习惯与Ecl ...
- 小程序——微信小程序初学踩过的坑
微信小程序初学踩过的坑 一.前言 最近因为某些需要和个人兴趣打算开发一下微信小程序,经过在官方网站上的基本了解,我大体知道了微信小程序开发的大致过程,其实最本质的就是MVVM,借用了很多模式上 ...
- Dcloud开发webApp踩过的坑
Dcloud开发webApp踩过的坑 一.总结 一句话总结:HTML5+扩展了JavaScript对象plus,使得js可以调用各种浏览器无法实现或实现不佳的系统能力,设备能力如摄像头.陀螺仪.文件系 ...
- Android使用WebView开发常见的坑
原文链接:http://mp.weixin.qq.com/s?__biz=MzAwODE1NTI2MQ==&tempkey=uP3a%2BOgIN7vPbLfJp3BTCl2KabYi1%2F ...
- apicloud地图、即时通讯、人脸识别登录、以及平时踩过得坑
apicloud技术浅谈 导语 apicloud 的学习也有一段时间了,这是我个人的一些经验,和踩过的坑,希望对大家能有一些帮助. apicloud的知识准备 apicloud 是一个用原生的思想搭建 ...
- 转:Flutter开发中踩过的坑
记录一下入手Flutter后实际开发中踩过的一些坑,这些坑希望后来者踩的越少越好.本文章默认读者已经掌握Flutter初步开发基础. 坑1问题:在debug模式下,App启动第一个页面会很慢,甚至是黑 ...
随机推荐
- 使用NPOI-创建Excel
这里简单的使用一下NPOI ,什么是NPOI? 既然你已经在需要使用了,就一定知道NPOI是干什么用的了. 开始正题吧. 我用控制台程序来给大家演示一下: 一.创建控制台程序 自行脑补 二.添加NPO ...
- 学习React系列(二)——深入了解JSX
1.JX实际上是React.createElement(component,props,...children)的语法糖 2.JSX判断是否为react组件的依据是标签首字母为大写(所以要求用户自定义 ...
- linux下多线程互斥量实现生产者--消费者问题和哲学家就餐问题
生产者消费者问题,又有界缓冲区问题.两个进程共享一个一个公共的固定大小的缓冲区.其中一个是生产者,将信息放入缓冲区,另一个是消费者,从缓冲区中取信息. 问题的关键在于缓冲区已满,而此时生产者还想往其中 ...
- X5 Blink下文字自动变大
在X5 Blink中,页面排版时会主动对字体进行放大,会检测页面中的主字体,当某一块的字体在我们的判定规则中,认为字体的字号较小,并且是页面中的主要字体,就会采用主动放大的操作.这显然不是我们想要的. ...
- python的字符串
首先,字符串是python内置的数据类型,其特点是用引号引起来,并且可以是使用单引号('字符串'),双引号("字符串"),三个引号('''字符串''' 和""& ...
- 实验吧_天下武功唯快不破&让我进去(哈希长度拓展攻击)
天下武功唯快不破 第一反应就去抓包,看到返回包的header中有FLAG的值,base64解码后得到下图所示 这就要求我们在请求头中post相应key的值,我直接在burp中尝试了多次都没有用,想起来 ...
- [PA 2014]Lustra
Description Byteasar公司专门外包生产带有镜子的衣柜.刚刚举行的招标会上,有n个工厂参加竞标.所有镜子都是长方形的,每个工厂能够制造的镜子都有其各自的最大.最小宽度和最大.最小高度. ...
- 决战 状压dp
决定在这个小巷里排兵布阵.小巷可以抽象成一个们彼此之间并不是十分和♂谐.具体来说,一个哲学家会有一个的矩形.每一位哲学家会占据一个格子.然而哲学家的01矩阵来表示他自己的守备范围.哲学家自己位于这个矩 ...
- SPOJ - DISUBSTR 多少个不同的子串
694. Distinct Substrings Problem code: DISUBSTR Given a string, we need to find the total number o ...
- LCT模板(BZOJ2631)
用LCT实现路径加,路径乘,断开及加上一条边(保证是树),查询路径和. #include <cstdio> #include <algorithm> #define l(x) ...