android launcher 之踩到的坑

來源:互聯網
上載者:User

標籤:ppi   one   這一   disable   key   visible   重啟   hint   ber   

需求:
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 startprivate 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 之踩到的坑

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.