Android10 设置默认Launcher

  1. 不保留系统launcher
    在mk文件中添加这个属性LOCAL_OVERRIDES_PACKAGES := Launcher3 对应的mk中删除,不让其编译即可。
  2. 保留系统默认的Launcher

当系统存在多个launcher时,若没有设置默认launcher,开机启动后会弹出提示框,罗列所有launcher,用户选择并设置了默认launcher后,按home键以及以后重启都会进入默认的launcher。亦或者在设置--》应用和通知--》默认应用--》主屏幕应用 中也可设置默认launcher。

客户希望系统能直接就进入我设定的launcher而不是弹出框后选择然后设置

前提:客户的app需要有home属性

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.HOME"/>
    <category android:name="android.intent.category.LAUNCHER"/>
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

修改方法:

首先我们需要增加全局属性Settings.Global.DEFAULT_LAUNCHER,,客户可通过修改该属性设置自定义桌面

1:增加自定义属性:
//frameworksbasecorejavaandroidproviderSettings.java
  public static final class Global extends NameValueTable {
        public static final String DEFAULT_LAUNCHER = "my_default_launcher";
  }

public static final class System extends NameValueTable {
     public static final String DEFAULT_LAUNCHER = Global.DEFAULT_LAUNCHER;
     PUBLIC_SETTINGS.add(DEFAULT_LAUNCHER);
     MOVED_TO_GLOBAL.add(Settings.Global.DEFAULT_LAUNCHER);
     
}
frameworksbasepackagesSettingsProvidersrccomandroidproviderssettingsDatabaseHelper.java
 private void loadGlobalSettings(SQLiteDatabase db) {
      loadStringSetting(stmt, Settings.Global.DEFAULT_LAUNCHER,
                    R.string.default_launcher);
 }               
//frameworksbasepackagesSettingsProviderresvaluesdefaults.xml
<string name="default_launcher" translatable="false">com.android.launcher3/com.android.launcher3.Launcher</string>

两种方法可选:

  • 修改弹出框界面直接设置launcher,弹出提示框界面是:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java:修改方法:在configureContentView内直接读取自定义的桌面,然后替代launcher3
1:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java 的configureContentView 读取设置的默认桌面,进行设置,mResolvingHome就是表示需要选择的是不是home
在这里只针对home作处理。
public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
        List<ResolveInfo> rList) {
  //....
    if (rebuildCompleted) {
        //....
        //phoebe add for default launcher for prop Settings.Global.DEFAULT_LAUNCHER
        if(mResolvingHome){
               String defaultlauncher = Settings.Global.getString(getContentResolver(), Settings.Global.DEFAULT_LAUNCHER);
        final TargetInfo defaultTarget = mAdapter.targetInfoForDefault(defaultlauncher);
        Log.d(TAG, "zmm add for configureContentView:defaultlauncher:" + defaultlauncher + ":" + defaultTarget);
        if (defaultTarget != null) {
            safelyStartActivity(defaultTarget);
            mPackageMonitor.unregister();
            mRegistered = false;
            finish();
            return true;
        }     
        }
        
    }
}
2:在ResolveListAdapter 增加targetInfoForDefault方法,并且在rebuildList成功以后,再次设置默认laucnher
    public class ResolveListAdapter extends BaseAdapter {
        @Nullable
public TargetInfo targetInfoForDefault(String info) {
    if (TextUtils.isEmpty(info)) {
        return null;
    }
    //com.yjx.inoexdash/com.yjx.inoexdash.ui.main.MainActivity
    String[] namesArray = info.trim().split("/");
    if (namesArray == null || namesArray.length < 2) {
        return null;
    }
    String packageName = namesArray[0];
    String activityInfo = namesArray[1];
    if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(activityInfo)) {
        return null;
    }
    ComponentName componentName = new ComponentName(packageName.trim(), activityInfo.trim());
    Log.d(TAG, "zmm add for targetInfoForDefault:" + componentName + ":" + mDisplayList.size());

    for (TargetInfo targetInfo : mDisplayList) {
        ComponentName targetComponentName = targetInfo.getResolvedComponentName();
        if (componentName.equals(targetComponentName)) {
            return targetInfo;
        }
    }
    return null;
}    

private void postListReadyRunnable() {
    if (mPostListReadyRunnable == null) {
        mPostListReadyRunnable = new Runnable() {
            @Override
            public void run() {
                //phoebe add for default launcher for prop Settings.Global.DEFAULT_LAUNCHER
               String defaultlauncher = mResolvingHome ? Settings.Global.getString(context.getContentResolver(), Settings.Global.DEFAULT_LAUNCHER) : null;
                final TargetInfo defaultTarget = TextUtils.isEmpty(defaultlauncher) ? null : mAdapter.targetInfoForDefault(defaultlauncher);
                Log.d(TAG, "zmm add for postListReadyRunnable defaultlauncher:" + defaultlauncher + ":" + defaultTarget);
                if (defaultTarget != null) {
                    safelyStartActivity(defaultTarget);
                    finish();
                    return;
                }
                setHeader();
                resetButtonBar();
                onListRebuilt();
                mPostListReadyRunnable = null;
            }
        };
        getMainThreadHandler().post(mPostListReadyRunnable);
    }
}
}    

修改ActivityManagerService 跳过ResolverActivity

1:ActivityStartController 的startHomeActivity方法内读取用户设置的桌面,并且跳过Settings,和Provision
因为第一次开机,会先Settings的CryptKeeper(加密),FallbackHome(设备正在启动中)接着是provision 的默认桌面,再是launcher3的Launcher页面
zmm add for startHomeActivity...com.android.settings.CryptKeeper:ComponentInfo{com.android.settings/com.android.settings.CryptKeeper}
   zmm add for startHomeActivity...com.android.settings.FallbackHome:ComponentInfo{com.android.settings/com.android.settings.FallbackHome}
   zmm add for startHomeActivity...com.android.provision.DefaultActivity:ComponentInfo{com.android.provision/com.android.provision.DefaultActivity}
    zmm add for startHomeActivity...com.android.launcher3.Launcher:ComponentInfo{com.android.launcher3/com.android.launcher3.Launcher}
//frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
        if (!ActivityRecord.isResolverActivity(aInfo.name)) {
            // The resolver activity shouldn't be put in home stack because when the foreground is
            // standard type activity, the resolver activity should be put on the top of current
            // foreground instead of bring home stack to front.
            options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
        }
        //phoebe add for default launcher for prop Settings.Global.DEFAULT_LAUNCHER skip settings,Provision
        String shortName = (intent == null || intent.getComponent() == null) ? null : intent.getComponent().getPackageName();
        if (!isSettingsApp(shortName) && !isProvisionApp(shortName) && setTargetActivityAsPreferredActivity()) {
            //Slog.d(TAG, " zmm add for  startHomeActivity as user seetings..");
            return;
        }
         //phoebe add for default launcher for prop Settings.Global.DEFAULT_LAUNCHER skip settings,Provision end
       ...
    }

//判断是不是Settings app
    private boolean isSettingsApp(String shortName) {
        return shortName != null && shortName.equals("com.android.settings");
    }

//判断是不是Provision app
    private boolean isProvisionApp(String shortName) {
        return shortName != null && shortName.equals("com.android.provision");
    }
//设置用户设置的桌面
    private boolean setTargetActivityAsPreferredActivity() {

        boolean result = false;
        Context context = mService.mContext;
        if (context == null)
            return result;
        String info = Settings.Global.getString(context.getContentResolver(), Settings.Global.DEFAULT_LAUNCHER);
        Slog.d(TAG, " zmm add for  setTargetActivityAsPreferredActivity info=" + info);
        if (TextUtils.isEmpty(info)) {
            return result;
        }
        String[] namesArray = info.split("/");
        if (namesArray == null || namesArray.length < 2) {
            return result;
        }
        String packageName = namesArray[0];
        String activityInfo = namesArray[1];
        if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(activityInfo)) {
            return result;
        }
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.MAIN");
        filter.addCategory("android.intent.category.HOME");
        filter.addCategory("android.intent.category.DEFAULT");

        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);

        packageName = packageName.trim();
        activityInfo = activityInfo.trim();
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
        final int N = list == null ? 0 : list.size();
        ComponentName[] set = new ComponentName[N];
        ComponentName componentName = null;
        int bestMatch = 0;
        for (int i = 0; i < N; i++) {
            ResolveInfo r = list.get(i);
            set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name);
            if (r.match > bestMatch) bestMatch = r.match;
            if (packageName.equals(r.activityInfo.packageName) && activityInfo.equals(r.activityInfo.name)) { /*modify target apk packageName  填写你的包名*/
                componentName = set[i];
            }
        }

//        Slog.e(TAG, "zmm add for setTargetActivityAsPreferredActivity newcomponentName=" + componentName);
        if (null != componentName) {
            pm.replacePreferredActivity(filter, IntentFilter.MATCH_CATEGORY_EMPTY, set, componentName);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
            result = true;
        }

        return result;
    }
2:接着修改frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,需要把用户在设置--》默认应用中选择的桌面应用保存到自定义的属性中。
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
            int userId) {
      ...
        for (int i = 0; i < resolveInfosSize; i++) {
            ResolveInfo resolveInfo = resolveInfos.get(i);

            if (resolveInfo.activityInfo != null && TextUtils.equals(
                    resolveInfo.activityInfo.packageName, packageName)) {
                        //phoebe add start
                ComponentName componentName = new ComponentName(resolveInfo.activityInfo.packageName,
                        resolveInfo.activityInfo.name);
                Slog.d(TAG, "zmm add for getHomeActivitiesAsUser:com:"+componentName);
                String value = resolveInfo.activityInfo.packageName + "/" + resolveInfo.activityInfo.name;
                android.provider.Settings.Global.putString(mContext.getContentResolver(),
                        android.provider.Settings.Global.DEFAULT_LAUNCHER, value);
                        //phoebe add end
                return componentName;
            }
        }
        return null;
    }

以上两种都可实现根据用户配置切换默认launcher

1:adb  切换
adb shell settings put global my_default_launcher com.android.launcher3/com.android.launcher3.Launcher
2:app可如此调用:
Settings.Global.putString(getContentResolver(), "my_default_launcher", "com.yjx.inoexdash/com.yjx.inoexdash.ui.main.MainActivity");

客户的app如果是普通的三方app,设置Global属性可能是报错没有权限:

Settings.Global.putString(getContentResolver(), "my_default_launcher", "com.yjx.inoexdash/com.yjx.inoexdash.ui.main.MainActivity");
报错:缺少权限:Manifest.permission.WRITE_SECURE_SETTINGS
该权限是系统app才能申请的权限,普通app申请不到的话,我们可以去掉该限制。
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java 
@@ -1249,18 +1250,23 @@ public class SettingsProvider extends ContentProvider {
     private boolean mutateGlobalSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode) {
-        // Make sure the caller can change the settings - treated as secure.
 -       enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
         
       
         String pkg = "com.android.settings"; //数据写入时使用系统settings应用的包名,系统会进行调用进程的包名判断
         if(!"my_default_launcher".equals(name)){  //phoebe add 自定义数据不进行权限验证
              pkg = "";
              // Make sure the caller can change the settings - treated as secure.
              enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
            }

//下面修改在插入操作时使用上面预设的包名,绕过包名验证
@@ -1268,7 +1274,7 @@ public class SettingsProvider extends ContentProvider {
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
                             UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
-                            getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
+                            "".equals(pkg)?getCallingPackage():pkg, forceNotify,  CRITICAL_GLOBAL_SETTINGS);
                 }

每日语录:滴水穿石!!!加油!!!

单曲循环《如愿》

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>