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又实现了两个接口

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
然后在各个情况判断下 当是文件 或者桌面小部件或者从widgets视图中拖拽时都让它不显示appinfo

    @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

接着判读是否是系统应用 因为系统应用是不能卸载的
从而控制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么 O(∩_∩)O哈哈~
因为按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 最后在
hideAppsCustomizeHelper和showAppsCustomizeHelper里面用到了
这个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主题。

这两个需求比较简单就不再分析了,如有需要请私信给我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容