在mk文件中添加这个属性LOCAL_OVERRIDES_PACKAGES := Launcher3 对应的mk中删除,不让其编译即可。
当系统存在多个launcher时,若没有设置默认launcher,开机启动后会弹出提示框,罗列所有launcher,用户选择并设置了默认launcher后,按home键以及以后重启都会进入默认的launcher。亦或者在设置--》应用和通知--》默认应用--》主屏幕应用 中也可设置默认launcher。
客户希望系统能直接就进入我设定的launcher而不是弹出框后选择然后设置
前提:客户的app需要有home属性
修改方法:
首先我们需要增加全局属性Settings.Global.DEFAULT_LAUNCHER,,客户可通过修改该属性设置自定义桌面
1:增加自定义属性:
//frameworks\base\core\java\android\provider\Settings.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);
}
frameworks\base\packages\SettingsProvider\src\com\android\providers\settings\DatabaseHelper.java
private void loadGlobalSettings(SQLiteDatabase db) {
loadStringSetting(stmt, Settings.Global.DEFAULT_LAUNCHER,
R.string.default_launcher);
}
//frameworks\base\packages\SettingsProvider\res\values\defaults.xml
com.android.launcher3/com.android.launcher3.Launcher
两种方法可选:
1:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java 的configureContentView 读取设置的默认桌面,进行设置,mResolvingHome就是表示需要选择的是不是home
在这里只针对home作处理。
public boolean configureContentView(List payloadIntents, Intent[] initialIntents,
List 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 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 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);
}
每日语录:滴水穿石!!!加油!!!
单曲循环《如愿》