Android Launcher

关于Lancher3,我一直觉得很复杂,没有时间去研究它(主要是懒),最近遇到项目的问题(赶鸭子上架),学习了一段时间,于是边记录自己的经验,边和大家共同进步.

Launcher3 和 Launcher3QuickStep 区别

一直迷糊这两个到底有啥区别,边看源码边找文档,终于搞明白了,先说区别,Launcher3QuickStep包含了recents,也就是最近启动的任务列表,可以清理,就是下面这个功能

当时解决问题的时候,单编的时候就有点懵了,应该编译哪个modue,于是找了bp 文件

// Build rule for Launcher3 app.
//
android_app {
    name: "Launcher3",
    static_libs: [
        "Launcher3CommonDepsLib",
    ],
    srcs: [
        "src/**/*.java",
        "src_shortcuts_overrides/**/*.java",
        "src_ui_overrides/**/*.java",
        "ext_tests/src/**/*.java",
    ],
    resource_dirs: [
        "ext_tests/res",
    ],
    //....
    overrides: [
        "Home",
        "Launcher2",
    ],
    //....
}

那应该是Launcher3 没错了,可是系统的目录并没有这个apk,显然不是了,后来又发现同目录显得android.mk

include $(CLEAR_VARS)
//....

LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3QuickStepLib
//.....

ifneq (,$(wildcard frameworks/base))
  LOCAL_PRIVATE_PLATFORM_APIS := true
else
  LOCAL_SDK_VERSION := system_current
  LOCAL_MIN_SDK_VERSION := 26
endif
LOCAL_PACKAGE_NAME := Launcher3QuickStep
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 //
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res

LOCAL_FULL_LIBS_MANIFEST_FILES := \
    $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
    $(LOCAL_PATH)/AndroidManifest-common.xml

LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*

//....
include $(BUILD_PACKAGE)

LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3

可以看到Launcher3QuickStep 是覆盖Launcher3 的,也就是说其实用的是Launcher3QuickStep 

看下编译文件build/make/target/product/handheld_system_ext.mk

 # /system_ext packages
 24 PRODUCT_PACKAGES += \
 25     Launcher3QuickStep \
 26     Provision \
 27     Settings \
 28     StorageManager \
 29     SystemUI \
 30     WallpaperCropper 

我贴地修改了为Launcher3 发现确实是没有了recents ,借鉴一下展讯平台的图

序号

Launcher模块名

OS

特性简介

备注

1

Launcher3

Android 9.0-Gc

提供基础的Home功能

会被其他形态的Launcher覆盖

2

Launcher3QuickStep

在Launchers基础上增加了Quickstep功能。

AOSP默认Launcher,适用于不带GMS 项目

3

Launcher3NoQsb

在Launcher3QuickStep基础上移除了首页固定的Google Search Bar

需配合GMS包编译,EEA版本专用

4

SearchLauncher

在Launcher3的基础上,增加了Google Feed (-1 Screen )

需配合GMS包编译,适用于带GMS项目

5

SearchLauncherQuickStep

在SearchLauncher的基础上增加了Quickstep1功能。

6

Launcher3Go

Android 9.0-Go

在Launcher3的基础上移除了Widget、Notification dots等功能。

适用于512M Ram项目

7

Launcher3QuickStepGo

在Launcher3Go基础上增加了QuickStepi功能

适用于1G Ram项目

8

Launcher3GoNoQsb

在Launcher3Go基础上移除了首页固定的Google Search Bar

需配合GMS包编译,EEA版本专用

ps 粉色部分是展讯平台特有的,适用于GMS ,平台为我们开发好了,其他部分是通用的

但是GMS版本Launcher 也有一个问题就是无论用那个版本,都会覆盖抽屉界面的搜索应用的框,这样又有点不太好了,感觉就很暴力(流氓),强制别人用google 的搜索,如何如修改回来呢,这个要感谢同事给的解决方法,后面会说.

但是无论GMS 版本还是非GMS版本,都有一个小问题

1,GMS 版本会覆盖抽屉界面的搜索应用的框,这样又有点不太好了,感觉就很暴力(流氓),强制别人用google 的搜索,如何如修改回来呢,这个要感谢同事给的解决方法思路,后面会说.

2,非GMS 版本倒是应用搜索框还在,但是又没有负一屏了,真是鱼和熊掌不可兼得呀,惆怅.

Launcher 目录功能分类

首页

找首页当然是去找Manifest 里面,里面只一个Launcher.java,没得挑了,就是他了,刚开始开着有点迷糊,里面就包含一个launcher.xml 抽屉布局.一层套一层,就注意一点就可以

    protected LauncherOverlayManager getDefaultOverlay() {
        return new LauncherOverlayManager() { };
    }

后面需要覆盖原生的界面,就可以继承这个进行overlay 原生布局

主页布局-提供主页的数据和工作去的文件夹

/**

* Runnable for the thread that loads the contents of the launcher:

* - workspace icons

* - widgets

* - all apps icons

* - deep shortcuts within apps

*/

com.android.launcher3.model.LoaderTask 加载所有应用的线程

代码是真多呀,我们先一步步解读下,

public void run() {
       //.......
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            loadWorkspace(allShortcuts);
            logASplit(logger, "loadWorkspace");

            // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
            // sanitizeData should not be invoked if the workspace is loaded from a db different
            // from the main db as defined in the invariant device profile.
            // (e.g. both grid preview and minimal device mode uses a different db)
            if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
                verifyNotStopped();
                sanitizeData();
                logASplit(logger, "sanitizeData");
            }

            verifyNotStopped();
            mResults.bindWorkspace();
            logASplit(logger, "bindWorkspace");

            mModelDelegate.workspaceLoadComplete();
            // Notify the installer packages of packages with active installs on the first screen.
            sendFirstScreenActiveInstallsBroadcast();
            logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");

            // Take a break
            waitForIdle();
            logASplit(logger, "step 1 complete");
            verifyNotStopped();

            // second step
            List<LauncherActivityInfo> allActivityList = loadAllApps();
            logASplit(logger, "loadAllApps");

            verifyNotStopped();
            mResults.bindAllApps();
            logASplit(logger, "bindAllApps");

            verifyNotStopped();
            IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
            setIgnorePackages(updateHandler);
            updateHandler.updateIcons(allActivityList,
                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                    mApp.getModel()::onPackageIconsUpdated);
            logASplit(logger, "update icon cache");

            if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
                verifyNotStopped();
                logASplit(logger, "save shortcuts in icon cache");
                updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                        mApp.getModel()::onPackageIconsUpdated);
            }

            // Take a break
            waitForIdle();
            logASplit(logger, "step 2 complete");
            verifyNotStopped();

            // third step
            List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
            logASplit(logger, "loadDeepShortcuts");

            verifyNotStopped();
            mResults.bindDeepShortcuts();
            logASplit(logger, "bindDeepShortcuts");

            if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
                verifyNotStopped();
                logASplit(logger, "save deep shortcuts in icon cache");
                updateHandler.updateIcons(allDeepShortcuts,
                        new ShortcutCachingLogic(), (pkgs, user) -> { });
            }

            // Take a break
            waitForIdle();
            logASplit(logger, "step 3 complete");
            verifyNotStopped();

            // fourth step
            List<ComponentWithLabelAndIcon> allWidgetsList =
                    mBgDataModel.widgetsModel.update(mApp, null);
            logASplit(logger, "load widgets");

            verifyNotStopped();
            mResults.bindWidgets();
            logASplit(logger, "bindWidgets");
            verifyNotStopped();

            updateHandler.updateIcons(allWidgetsList,
                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
                    mApp.getModel()::onWidgetLabelsUpdated);
            logASplit(logger, "save widgets in icon cache");

            // fifth step
            if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                loadFolderNames();
            }

            verifyNotStopped();
            updateHandler.finish();
            logASplit(logger, "finish icon update");

            mModelDelegate.modelLoadComplete();
            transaction.commit();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            logASplit(logger, "Cancelled");
        } finally {
            logger.dumpToLog();
        }
        TraceHelper.INSTANCE.endSection(traceToken);
    }

google 工程师其实也加了注释,总体是这五步

/packages/apps/Launcher3 / src/com/android/launcher3/LauncherProvider.java

/**
     * Loads the default workspace based on the following priority scheme:
     *   1) From the app restrictions
     *   2) From a package provided by play store
     *   3) From a partner configuration APK, already in the system image
     *   4) The default configuration for the particular device
     */
    synchronized private void loadDefaultFavoritesIfNecessary() {
        SharedPreferences sp = Utilities.getPrefs(getContext());

        if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
            Log.d(TAG, "loading default workspace");

            AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
            if (loader == null) {
                loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
            }
            if (loader == null) {
                final Partner partner = Partner.get(getContext().getPackageManager());
                if (partner != null && partner.hasDefaultLayout()) {
                    final Resources partnerRes = partner.getResources();
                    int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                            "xml", partner.getPackageName());
                    if (workspaceResId != 0) {
                        loader = new DefaultLayoutParser(getContext(), widgetHost,
                                mOpenHelper, partnerRes, workspaceResId);
                    }
                }
            }

            final boolean usingExternallyProvidedLayout = loader != null;
            if (loader == null) {
                loader = getDefaultLayoutParser(widgetHost);
            }

            // There might be some partially restored DB items, due to buggy restore logic in
            // previous versions of launcher.
            mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
            // Populate favorites table with initial favorites
            if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
                    && usingExternallyProvidedLayout) {
                // Unable to load external layout. Cleanup and load the internal layout.
                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
                        getDefaultLayoutParser(widgetHost));
            }
            clearFlagEmptyDbCreated();
        }
    }

对应的xml

packages/apps/Launcher3/res/xml/default_workspace_*.xml

看注释,应该是默认加载的首页应用布局,可以在上面xml 修改自己的默认.修改位置,数据库有数据,读取数据库,没有读取默认

抽屉页面

找不到的话就先找布局,通过搜索框里面的关键字,"Search apps",对应布局packages/apps/Launcher3/res/layout/secondary_launcher.xml 这个就是抽屉页面的主要布局

对应Activity com.android.launcher3.secondarydisplay

这里面主要注意的是,搜索应用框,后面定制需要注意

<com.android.launcher3.allapps.search.AppsSearchContainerLayout
            android:id="@id/search_container_all_apps"
            android:layout_width="match_parent"
            android:layout_height="@dimen/all_apps_search_bar_field_height"
            android:layout_centerHorizontal="true"
            android:layout_gravity="top|center_horizontal"
            android:background="@drawable/bg_all_apps_searchbox"
            android:elevation="1dp"
            android:focusableInTouchMode="true"
            android:gravity="center"
            android:hint="@string/all_apps_search_bar_hint"
            android:imeOptions="actionSearch|flagNoExtractUi"
            android:inputType="text|textNoSuggestions|textCapWords"
            android:maxLines="1"
            android:padding="8dp"
            android:saveEnabled="false"
            android:scrollHorizontally="true"
            android:singleLine="true"
            android:textColor="?android:attr/textColorSecondary"
            android:textColorHint="@drawable/all_apps_search_hint"
            android:textSize="16sp" />

app 视图布局

com.android.launcher3.allapps.AllAppsContainerView

整理了一下遇到的问题

[Q1]搜索应用返回键无法清除输入框状态

packages/apps/Launcher3

diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 21bc479..64bf1d0 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -117,6 +117,9 @@
     }

     public void hideKeyboard() {
+        clearFocus();
         hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
     }

[Q2]隐藏首页的google 搜索组件

diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1b6001f..e7599ae 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -53,7 +53,9 @@
      * and should be modified at a project level.
      */
+    public static boolean QSB_ON_FIRST_SCREEN  = false;
     /**

[Q3]文件夹中放入三个应用,图标溢出边界

diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 8cd91d3..042559f 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -130,7 +130,9 @@
     public float scaleForItem(int numItems) {
         // Scale is determined by the number of items in the preview.
         final float scale;
-        if (numItems <= 3) {
+        if (numItems < 3) {
             scale = MAX_SCALE;
         } else {
             scale = MIN_SCALE;

[Q4]GMS 覆盖原生framework res 配置路径

overlay/GmsConfigOverlayCommon/res/values/config.xml

[Q5]GMS 覆盖原生app 和service

有其他需要覆盖的,可以对应应用的路径,在gms_overlay 下面创建文件覆盖即可

[Q6]GMS 版本无搜索应用框

前面也说道了,GMS 编译的是SearchlauncherQuickStep,修改vendor/partner_gms

1,SearchLauncher+ 原生SearchLauncher.java

diff --git a/apps/SearchLauncher/Android.mk b/apps/SearchLauncher/Android.mk
index 1dd35fc..9dd9cfa 100644
--- a/apps/SearchLauncher/Android.mk
+++ b/apps/SearchLauncher/Android.mk
@@ -23,32 +23,43 @@
 LAUNCHER_PATH := ../../../../packages/apps/Launcher3
 
 LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib Launcher3QuickStepLib
-LOCAL_STATIC_JAVA_LIBRARIES := lib_launcherClient
+LOCAL_STATIC_JAVA_LIBRARIES := lib_launcherClient \
+                                   SystemUISharedLib \
+                                SystemUI-statsd
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, $(LAUNCHER_PATH)/src_shortcuts_overrides) \
-    $(call all-java-files-under, $(LAUNCHER_PATH)/src_ui_overrides) \
     $(call all-java-files-under, src) \
-    $(call all-java-files-under, src_base) \
-    $(call all-java-files-under, $(LAUNCHER_PATH)/src)
+    $(call all-java-files-under, src_sim_base) \
+    $(call all-java-files-under, $(LAUNCHER_PATH)/src) \
+    $(call all-java-files-under, $(LAUNCHER_PATH)/quickstep/src)
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+                      $(LOCAL_PATH)/$(LAUNCHER_PATH)/quickstep/res
 
 LOCAL_PROGUARD_FLAG_FILES := $(LAUNCHER_PATH)/proguard.flags
 
-LOCAL_MIN_SDK_VERSION := 28
-LOCAL_SDK_VERSION := current
+
+ifneq (,$(wildcard frameworks/base))
+  LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+  LOCAL_SDK_VERSION := system_current
+  LOCAL_MIN_SDK_VERSION := 28
+endif
+
 LOCAL_PACKAGE_NAME := SearchLauncher
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT2_ONLY := true
+
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_SYSTEM_EXT_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
 
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/$(LAUNCHER_PATH)/quickstep/AndroidManifest.xml
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD SPDX-license-identifier-GPL SPDX-license-identifier-MIT
 LOCAL_LICENSE_CONDITIONS := notice restricted
 include $(BUILD_PACKAGE)
diff --git a/apps/SearchLauncher/src_sim_base/com/android/searchlauncher/SearchLauncher.java b/apps/SearchLauncher/src_sim_base/com/android/searchlauncher/SearchLauncher.java
new file mode 100644
index 0000000..cb7180e
--- /dev/null
+++ b/apps/SearchLauncher/src_sim_base/com/android/searchlauncher/SearchLauncher.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * SIM1FU010 add for PM-65101 by 00467 at 20220426
+ */
+
+package com.android.searchlauncher;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
+
+public class SearchLauncher extends QuickstepLauncher {
+
+    @Override
+    protected LauncherOverlayManager getDefaultOverlay() {
+        return new OverlayCallbackImpl(this);
+    }
+}
diff --git a/products/gms.mk b/products/gms.mk
index 50f2695..e823e07 100644
--- a/products/gms.mk
+++ b/products/gms.mk
@@ -97,7 +97,7 @@
 # GMS sample application packages
 PRODUCT_PACKAGES += \
     GmsSampleIntegration \
-    SearchLauncherQuickStep
+    SearchLauncher
 
 include $(ANDROID_PARTNER_GMS_HOME)/products/gms_package_version.mk
 
diff --git a/products/gms_eea_v2_type4c.mk b/products/gms_eea_v2_type4c.mk
index d91d059..80f6ac1 100644
--- a/products/gms_eea_v2_type4c.mk
+++ b/products/gms_eea_v2_type4c.mk
@@ -7,4 +7,4 @@
     Chrome \
     SearchSelector \
     GmsEEAType4cIntegration \
-    SearchLauncherQuickStep
+    SearchLauncher

2,SearchLauncherQuickStep + search 框

diff --git a/apps/SearchLauncher/quickstep/res/layout/search_container_all_apps.xml b/apps/SearchLauncher/quickstep/res/layout/search_container_all_apps.xml
index 1fae132..d6784c6 100644
--- a/apps/SearchLauncher/quickstep/res/layout/search_container_all_apps.xml
+++ b/apps/SearchLauncher/quickstep/res/layout/search_container_all_apps.xml
@@ -49,5 +49,5 @@
         android:textColor="?android:attr/textColorSecondary"
         android:textColorHint="@drawable/all_apps_search_hint"
         android:textSize="16sp"
-        android:visibility="invisible" />

只是根据根据自己遇到的问题,由浅入深慢慢分析,写的不对的,还请指正,后续会继续研究Launcher,持续更新.

参考文献

https://bbs.16rd.com/thread-573080-1-1.html

https://blog.csdn.net/qq_37207639/article/details/88893640

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值