1.Launcher界面简介
2.Launcher初始化网格
3.Launcher:defaultLayoutId加载的是default_workspace_5x5,还是partner_default_layout.xml
4.如何客制化default_desktop_grid_name
一 .Launcher界面包含Google Search(这是个伪widget),Workspace,Hotseat,Folders。
二 Launcher初始化网格代码:
packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java
packages/apps/Launcher3/res/xml/device_profiles.xml
packages\apps\Launcher3\src\com\sprd\ext\grid\DesktopGridModel.java
/** 1.gridName值是从DesktopGridModel.java中获得,
* mDefaultGridName = context.getString(R.string.default_desktop_grid_name);
* 2.我们如果需要使用已经存在的grid-option,客制化Launcher几行几列,
* 可以直接修改该值:default_desktop_grid_name
* 例如:<string name="default_desktop_grid_name" translatable="false">"6_by_5"</string>
*/
private String initGrid(Context context, String gridName) {
Log.e("kl", "initGrid **1");
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
Point smallestSize = new Point();
Point largestSize = new Point();
display.getCurrentSizeRange(smallestSize, largestSize);
/** 1.getPredefinedDeviceProfiles函数中会加载
* device_profiles.xml文件,该函数中真正对定义了我们的workspace风格,
* 包含3x3,4x4,5x5,defaultLayoutId 等重要信息。
* 最后会通过参数gridName 过滤出我们项目所要用的。
*/
ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
// This guarantees that width < height
float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
// Sort the profiles based on the closeness to the device size
Collections.sort(allOptions, (a, b) ->
Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
DisplayOption interpolatedDisplayOption =
invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
GridOption closestProfile = allOptions.get(0).grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
mExtraAttrs = closestProfile.extraAttrs;
if (!closestProfile.name.equals(gridName)) {
Utilities.getPrefs(context).edit()
.putString(mIdpGridKey, closestProfile.name).apply();
}
if (mMonitor.getSRController() != null) {
mMonitor.getSRController().saveGridNameIntoStorage(context, closestProfile.name);
}
iconSize = interpolatedDisplayOption.iconSize;
iconShapePath = getIconShapePath(context);
landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDisplayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
//这个地方可能修改我们的几行几列...需要自己看下自己的代码。
applyPartnerDeviceProfileOverrides(context, dm);
Point realSize = new Point();
display.getRealSize(realSize);
// The real size never changes. smallSide and largeSide will remain the
// same in any orientation.
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
FolderIconController fic = mMonitor.getFolderIconController();
if (fic != null) {
fic.backupOriginalFolderRowAndColumns(numFolderRows, numFolderColumns);
fic.updateFolderRowAndColumns(this);
}
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
defaultWallpaperSize = new Point(
(int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
largeSide);
} else {
defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
}
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
mDisplayOptionName = interpolatedDisplayOption.name;
mGridName = closestProfile.name;
Log.d("kl","initGrid closestProfile.name"+mGridName);
return closestProfile.name;
}
三 .Launcher:defaultLayoutId加载的是default_workspace_5x5,还是partner_default_layout.xml
packages\apps\Launcher3\NativeLauncherLayout\res\xml\partner_default_layout.xml
packages/apps/Launcher3/src/com/android/launcher3/Partner.java
如果从上面的代码来看,最后调用的是device_profiles.xml来加载布局文件。
<grid-option
launcher:name="3_by_3"
launcher:numRows="3"
launcher:numColumns="3"
launcher:numFolderRows="2"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="3"
<!-- Launcher界面缺省定义的hotseat,icon,folders,widget 都在这里面定义-->
launcher:defaultLayoutId="@xml/default_workspace_3x3" >
<display-option
launcher:name="Super Short Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="300"
launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
<display-option
launcher:name="Shorter Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="400"
launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
</grid-option>
我的项目最后调用的却是partner_default_layout.xml,这是为什么?
还记得上面代码中initGrid函数 函数中调用了applyPartnerDeviceProfileOverrides方法,该方法是这样的:
private static final String
ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
private void applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm) {
//Partner.get()会去做什么?它会去寻找系统中匹配的apk
Partner p = Partner.get(context.getPackageManager());
if (p != null) {
p.applyInvariantDeviceProfileOverrides(this, dm);
}
}
/**
* Find and return partner details, or {@code null} if none exists.
*/
public static synchronized Partner get(PackageManager pm) {
Log.e("kl", "Partner findSystemApk from action **1");
if (!sSearched) {
Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
if (apkInfo != null) {
sPartner = new Partner(apkInfo.first, apkInfo.second);
}
sSearched = true;
}
return sPartner;
}
最后匹配到的就是:
packages\apps\Launcher3\NativeLauncherLayout\AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 Unisoc Inc. All Rights Reserved. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unisoc.launcher.customization">
<application android:label="@string/app_label">
<!-- This isn't a real receiver, it's only used as a marker interface. -->
<receiver android:name=".LauncherCustomizationReceiver">
<intent-filter>
<action android:name="com.android.launcher3.action.PARTNER_CUSTOMIZATION" />
</intent-filter>
</receiver>
</application>
</manifest>
NativeLauncherLayout是一个单独的模块,在该模块下就定义了:
packages\apps\Launcher3\NativeLauncherLayout\res\xml\partner_default_layout.xml
我们在继续看下面的代码:
packages\apps\Launcher3\src\com\android\launcher3\LoaderTask.java
packages\apps\Launcher3\src\com\android\launcher3\LauncherProvider.java
在Launcher加载的时候,会执行到LoaderTask.java文件中的loadWorkspace()函数。
在loadWorkspace函数中会调用如下代码:
LogUtils.d(TAG, "loadWorkspace: loading default favorites");
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES又做了什么呢?
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
loadDefaultFavoritesIfNecessary();
return null;
}
synchronized private void loadDefaultFavoritesIfNecessary() {
if (getFlagEmptyDbCreated(getContext(), mOpenHelper.getDatabaseName())) {
Log.d("kl", "loading default workspace");
AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
if (loader == null) {
Log.e("kl", "loadDefaultFavoritesIfNecessary **1");
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();
//这里会去Partner文件中获取RES_DEFAULT_LAYOUT
//而RES_DEFAULT_LAYOUT就是RES_DEFAULT_LAYOUT = "partner_default_layout";
int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
"xml", partner.getPackageName());
if (workspaceResId != 0) {
loader = new DefaultLayoutParser(getContext(), widgetHost,
mOpenHelper, partnerRes, workspaceResId);
Log.e("kl", "workspaceResId **1");
}
}
}
.........
}
四 项目中用的是google的SearchLauncher,但是从Android.mk脚本看,大多数代码都是用到Launcher3的代码。
如果要overlay 客制化某些属性值的话,应该如何客制化?
就拿客制化workspace是用4x4,还是5x5来说,要定制的话,按正常情况下直接overlay下面路径就行,
packages\apps\Launcher3\res\values\config_ext.xml
<string name="default_desktop_grid_name" translatable="false">"6_by_5"</string>
测试了下,发现没有起效果,那需要如何做?
正确的做法是要像我下面这样overlay才有效果:
这里直接写下overlay的路径,一看就明白了,
mocor_AndroidQ\zrevo\e64_xx16-ruiou\overlay\current\vendor\google\apps\SearchLauncher\res\values\config_ext.xml