Android11-Android15-Launcher3去抽屉功能-定制应用-另辟蹊径方案

提示: 核心需求就是Launcher3 去抽屉功能实现,要把所有抽屉应用放到桌面上。

文章目录


前言-需求

直接看目前需求吧:要实现如下公版软件需求,细节需求:

更多应用完全用原生Launcher3 的效果。

需求思路

所以针对如上需求,最直接的就是修改原生Launcher,原生Launcher3 直接去抽屉,然后点击更多应用跳转到原生Launcher3 即可。

一、方案选型

1、更多应用-在原生Launcher3 基础上修改

这种方案其实最直接的,最优效果,完全直接使用Launcher3 的效果。 直接跳转到原生Launcher3 的更多应用界面(抽屉模块);或者跳转到Launcher3 默认的主界面,但是主界面需要把所有应用显示出来同时去掉抽屉。

参考资料

Android11-Launcher3 定制-去除副屏幕-可以滑动效果

Android11-Launcher3 定制-去除副屏幕-可以滑动效果 - 篇二

或者其他网上很多参考资料,可以参考。

难点-困难点-说明

这种直接在原生Launcher3 上面修改方案,肯定最直接最优解。 但是难度系数很大,如果你不是很熟悉Launcher3 源码,或者 还没有研究过、做过Launcher3 相关开发 或者没有研究过。那么就需要花点时间 熟悉架构、熟悉不同Android版本流程,进行Launcher3 定制。

  • 不同Android版本,Launcher3 对应的源码有稍微区别,遇到问题现象也不一样的,对于快捷方式的代码需要稍微修改,所以需要你熟悉Launcher3 源码 这个是必须条件。
  • 遇到普遍最大问题是: 桌面为空,无法显示出来应用,主界面第一次无法创建盒子布局,重启后才会出现;桌面无法显示已经安装的快捷应用。

只有实际开发中才会遇到各种问题,不同Android 会遇到不一样的问题,需要针对性修改实现。

2、更多应用自己写一个

第二种方案是自己写一个更多应用,加载出所有App 不就行了嘛。 这种方案是最简单的,但是体验最差,达不到Launcher3 的各种功能和UI效果。

3 、把原生Launch3 从系统源码分离出来-二次开发Launcher3源码-实现效果

网上有相关已经去掉抽屉的原生Launcher,直接在这些Launcher上面开发实现更多应用效果,更改包名不要和原生Launcher3 重名。 那么 需要做的就是:

  • 原生Launcher3 作为普通应用(去掉Home属性)、或者 原生Launcher3 不默认为主界面Home程序。
  • 定制二次开发的Launcher3 更改为普通的更多应用App 。
  • 主Home程序写一个简单的调用

二、方案实现

定制Launcher3 二次修改作为 更多应用App

源码路径: Launcher3 去抽屉功能

实现效果如下:

更多应用-主界面效果

更多应用-可拖拽效果

更多应用-文件夹效果

更多应用-长按-应用信息显示

更多应用本质

更多应用实际上就是一个定制版的Launcher3 程序,然后去掉Home 属性,其它应用直接打开显示就是更多应用效果。 如果把Home 程序加上,它实际上就是一个定制版本的Launcher3 程序。

三、方案定制过程中遇到的坑点-困难点

1、直接更改Launcher3 实现更多应用效果

第一次开机无法显示快捷到主界面

Android11 网上是有资料的可以实现,但是 Android15 目前网上资料比较少 可以修改成功。但是第一次 无法显示更多应用快捷到主界面。

2、默认自己Launcher-定制Launcher3 作为更多应用- 原生Launcher3 去除Home属性

底部虚拟按键-最近历史任务快捷键失效

因为自己开发了Launcher,那么开机启动就需要启动自己的Launcher,所以直接把Launcher3 的Home 属性去掉即可。 实际发现去掉之后 虚拟按键-最近历史任务 按键失效。 在MTK平台上面是没有问题的,但是在Rk Android11 和 Android15 上面都有问题的。

3、默认自己Launcher-定制Launcher3 作为更多应用- 原生Launcher3 不去除Home属性

开机后会让选择主程序界面-系统恢复出厂设置还是要选择Home 程序默认

多Launcher 界面,会让选择Launcher 程序,让选择默认Home 程序。

多Launcher 实现默认主HOME 程序

这里面有两个知识点:

  • 开机默认打开主程序
  • 点击HOME 程序,一定要返回到自己开发的Launcher 程序,不可回到系统原生Launcher3程序

四、个人经验建议-项目需求实现流程

经验-建议:如果是应用开发者-Launcher3经验一般-建议定制开发Launcher3 修改好的App源码修改

如果你是应用开发者,定制Launcher3 是有难度的,直接拿提供的资料和网上资料参考,对应用进行定制实现更多应用功能。 Launcher3 去抽屉功能。 不建议 直接修改系统源码Launcher3,定制有一定难度的。

解决虚拟按键点击无用问题

Rk平台中,无论Android11 还是 Android15 都要求有原生Launcher3 存在的,虚拟按键才能正常工作。所以集成自己定制Launcher 程序,系统原生自带Launcher3 必须要有Home 属性。

默认自己Launcher 作为默认默认Home程序

强烈参考之前源码解读和思路解读:Android 动态设置默认Launcher(默认应用 电话-短信-浏览器-主屏幕应用))

说白了如何去设置指定的Launcher作为默认Home 程序。 自己就放在自己应用里面吧,在App 里面Application 实现,设置自己作为默认HOME 程序。

java 复制代码
   private fun setDefaultHome() {



       val ISFirstLaunch= MmkvHelper.getInstance().mmkv.getInt(IS_FISRT_LAUNCHER_APP,0)

        Log.d(TAG,"=========ISFirstLaunch:$ISFirstLaunch======")
        if(ISFirstLaunch==0){
            Log.d(TAG,"========= 第一次启动,重新设置 HOME 程序======")

            val roleManager: RoleManager =
                ContextProvider.get().context.getSystemService<RoleManager>(RoleManager::class.java)
            val executor: Executor = ContextProvider.get().context.getMainExecutor()
            val callback =
                Consumer<Boolean> { successful: Boolean ->
                    if (successful) {
                        Log.i(TAG, " 成功了==> Package " + "added" + " as role holder, role: " + "测试Home 桌面" + ", package: "  )
                    } else {
                        Log.i(TAG, " 失败了==>  Package " + "added" + " as role holder, role: " + "测试Home 桌面" + ", package: "  )
                    }
                }
            val addRoleHolderAsUser = roleManager.javaClass.getMethod(
                "addRoleHolderAsUser",
                String::class.java, String::class.java, Int::class.java,
                UserHandle::class.java, Executor::class.java, Consumer::class.java
            )
            addRoleHolderAsUser.isAccessible = true
            val userHandle = NavigationHelper.newInstance(
                UserHandle::class.java, arrayOf<Class<*>?>(
                    Int::class.javaPrimitiveType
                ), 0
            ) as UserHandle
            addRoleHolderAsUser.invoke(roleManager, "android.app.role.HOME", "com.fise.dmseries",
                1,userHandle,executor,callback)

            MmkvHelper.getInstance().mmkv.putInt(IS_FISRT_LAUNCHER_APP,1);
        }else{
            Log.d(TAG,"=========非 第一次启动 不处理======")

        }
    }

多Launcher 解决 多Home程序选择问题

直接在对应界面 结束当前界面并跳转到自己定制的Launcher.

Android11 解决方案
多个Launcher选择界面直接跳转到指定Home 程序

直接在Home 选择界面跳转到自己的Home程序不就行了嘛

路径:base/core/java/com/android/internal/app/ResolverActivity.java

java 复制代码
diff --git a/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java b/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
index fadc15912d..01fb5bf497 100644
--- a/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
+++ b/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
@@ -102,6 +102,13 @@ import java.util.Objects;
 import java.util.Set;


+// modify by fangchen start
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+// modify by fangchen end
+
+
 /**
  * This activity is displayed when the system attempts to start an Intent for
  * which there is more than one matching activity, allowing the user to decide
@@ -343,7 +350,51 @@ public class ResolverActivity extends Activity implements
             List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
         setTheme(appliedThemeResId());
         super.onCreate(savedInstanceState);
-
+
+
+       // modify by fangchen start
+
+
+             try {
+           // Intent intent = getIntent();
+            if (intent != null && Intent.ACTION_MAIN.equals(intent.getAction())
+                    && intent.hasCategory(Intent.CATEGORY_HOME)) {
+
+
+                ComponentName myHomeComponent = new ComponentName(
+                        "com.fise.dmseries",
+                        "com.fise.dmseries.ui.main.MainActivity"
+                );
+
+
+              /*  PackageManager pm = getPackageManager();
+                pm.setPreferredActivity(
+                        intent,
+                        IntentFilter.MATCH_CATEGORY_TYPE,
+                        null,
+                        myHomeComponent,
+                        UserHandle.USER_CURRENT
+                );
+               */
+
+                intent.setComponent(myHomeComponent);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(intent);
+
+
+                finish();
+                return;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+       // modify by fangchen end
+
+
+
+
Android15 解决方案

其实在Android15 上面 沿用Android11 的方案也是可以的,这里给出另外一种方案

java 复制代码
 modified:   base/services/core/java/com/android/server/policy/PhoneWindowManager.java  【点击HOME 按键跳转到指定Home 程序】
 modified:   base/services/core/java/com/android/server/wm/RootWindowContainer.java 【开机直接跳转到 指定HOME程序】
点击Home 按键 回到 首页-定制首页

这里其实是多余操作,因为开机后跳转到指定 HOME 程序,指定HOME程序第一次打开会去指定默认HOME程序的,所有点击HOME程序一定回到指定HOME程序的。 但是这里映射按键点击触发知识点蛮多的,都是在这个类里面定制修改 业务逻辑的。

java 复制代码
--- a/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3518,7 +3518,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {

         switch (keyCode) {
             case KeyEvent.KEYCODE_HOME:
-                return handleHomeShortcuts(focusedToken, event);
+                // modify by  fangchen start
+                               //return handleHomeShortcuts(focusedToken, event);
+
+                       Intent intentHome = new Intent();
+            intentHome.setComponent(new ComponentName(
+            "com.fise.dmseries",
+            "com.fise.dmseries.ui.main.MainActivity"
+              ));
+            intentHome.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intentHome);
+                       Log.d(TAG, "====interceptSystemKeysAndShortcuts==== KeyEvent.KEYCODE_HOME==== = ");
+
+                       return true;
+                               // modify by fangchen end
+
             case KeyEvent.KEYCODE_RECENT_APPS:
                 if (firstDown) {
                     showRecentApps(false /* triggeredFromAltTab */);
开机默认跳转到指定HOME程序

保证每次开机跳转到指定HOME程序

java 复制代码
+// modify by fangchen start
+import android.util.Log;
+// modify by fangchen end
+
+
 /** Root {@link WindowContainer} for the device. */
 class RootWindowContainer extends WindowContainer<DisplayContent>
         implements DisplayManager.DisplayListener {
@@ -1484,7 +1489,68 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
      */
     @VisibleForTesting
     ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
-        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+
+
+
+
+ // modify by fangchen start
+        ComponentName myHome = new ComponentName(
+                "com.fise.dmseries",
+                "com.fise.dmseries.ui.main.MainActivity");
+        ActivityInfo aInfo = null;
+        try {
+            aInfo = mService.getPackageManagerInternalLocked()
+                    .getActivityInfo(myHome,
+                            android.content.pm.PackageManager.GET_ACTIVITIES
+                                    | android.content.pm.PackageManager.MATCH_SYSTEM_ONLY,
+                            userId, 0);
+
+        } catch (Exception e) {
+         }
+
+
+        if (aInfo != null) {
+            aInfo = new ActivityInfo(aInfo);
+            aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+             return aInfo;
+        }
+
+         final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+        final ComponentName comp = homeIntent.getComponent();
+        aInfo = null;
+        try {
+            if (comp != null) {
+                // Factory test.
+                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+            } else {
+                final String resolvedType =
+                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+                final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
+                        userId, flags, Binder.getCallingUid(), Binder.getCallingPid());
+                if (info != null) {
+                    aInfo = info.activityInfo;
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
+        }
+
+        if (aInfo == null) {
+            Slogf.wtf(TAG, new Exception(), "No home screen found for %s and user %d", homeIntent,
+                    userId);
+            return null;
+        }
+
+        aInfo = new ActivityInfo(aInfo);
+        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+         return aInfo;
+
+
+
+               // modify by fangchen end
+
+
+        /*final int flags = ActivityManagerService.STOCK_PM_FLAGS;
         final ComponentName comp = homeIntent.getComponent();
         ActivityInfo aInfo = null;
         try {
@@ -1512,7 +1578,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>

         aInfo = new ActivityInfo(aInfo);
         aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
-        return aInfo;
+        return aInfo;*/
     }

     @VisibleForTesting
解决方案-小结
  • 如上,其实Android11 解决方案也有弊端的,也需要跟Android15 一样,设置系统开机一定是去启动默认地址的Launcher HOME程序。不然 在系统设置里面点击了 原生Launcher3 作为HOME程序,那么就没法实现开机默认打开定制HOEM程序了。
  • 不同的Android 版本,系统 Framework层API稍有改动不一致,遇到实际问题自己思考,实现需求,定制源码。

总结

  • 这个需求里面涉及到的Launcher3 知识点其实是海量的,蛮多的,如果有兴趣可以自行研究。
  • Launcher3 定制开发一个更多应用功能。对于初学者或者Framework初学者 更能轻松入局,解决实际项目需求。 Launcher3 去抽屉功能
  • 如果有时间,可以研究Launcher3 源码,这里面有大量的Launcher3 架构、绑定数据、数据加载、UI效果知识点,不同版本 还有区别,需要深入研究。
  • 上面提供的定制Launcher3 开发 更多应用最大的益处 不用进行系统开发多版本适配,直接用。