实现Launcher3 桌面屏蔽部分内容,不让显示
文章目录
- 修改文件路径-实现方式
- 基础-源码模块配置
- Launcher3 源码位置
- 编译模块配置
- 配置如下参数 属性配置:
- 默认的Launcher3 选项配置
- GMS的Launcher3 配置
- 第三方Launcher需要默认为Launcher时候-系统Launcher3 的配置
- 参考资料
- 实现方案
- 源码分析
- Launcher
- LauncherModel
- addCallbacksAndLoad 方法
- startLoader
- LoaderResults
- BaseLoaderResults
- bindAllApps
- AllAppsList
- 修改实现-举例说明
- 总结
修改文件路径-实现方式
/vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/model/AllAppsList.java
在copyData 方法中过滤
public AppInfo[] copyData() {
AppInfo[] result = data.toArray(EMPTY_ARRAY);
/* Arrays.sort(result, COMPONENT_KEY_COMPARATOR);
return result;*/
List<AppInfo> updatedAppInfos = new ArrayList<>();
for(AppInfo info:result){
if(info.componentName!=null){
if(info.componentName.getPackageName().equals("com.android.documentsui")){
Log.d(TAG,"info. filter :"+info.componentName.getPackageName());
}else{
Log.d(TAG,"info nofilter:"+info.componentName.getPackageName());
updatedAppInfos.add(info);
}
}
}
AppInfo[] result1= updatedAppInfos.toArray(new AppInfo[0]);
Arrays.sort(result1, COMPONENT_KEY_COMPARATOR);
return result1;
}
基础-源码模块配置
源码里面的Launcher3 有 非GMS的和GMS的,特别对于MTK、高通、展讯 手机相关平台方案,两个类型Launcher3 模块共存的。 对于 RK 、全志下的大多工控产品下,源码是没有 GMS一说的。
Launcher3 源码位置
MTK 平台 非GMS 源码路径如下:
/vendor/mediatek/proprietary/packages/apps/Launcher3
MTK 平台 GMS包 下
/vendor/google/apps/SearchLauncher
/vendor/google/overlay/gms_overlay/vendor/google/apps/SearchLauncher
在GMS 情况下,为什么会有两个 SearchLauncher, 这是系统的机制而已, gms_overlay 文件夹下的app 包会覆盖非GMS的软件包。
编译模块配置
路径:
\device\mediatek\system\common\device.mk
配置如下参数 属性配置:
默认的Launcher3 选项配置
#PRODUCT_PACKAGES += Launcher3
如果要使用MTK的GMS 包的 Launcher ,可以配置如下
#PRODUCT_PACKAGES += MtkLauncher3QuickStep
对应的源码位置是如下两个,具体可以查阅下并进行实验:
/vendor/google/apps/SearchLauncher
/vendor/google/overlay/gms_overlay/vendor/google/apps/SearchLauncher
其实这样配置的Launcher3 本身是属于GMS的,所以发现实际跑起来的Launcher是gms包,还带有开机向导。
GMS的Launcher3 配置
GMS 情况下,配置好GMS编译环境即可, 这样就会加载 gms 文件下的所有app 包,里面就有gms 包的Launcher3
\device\mediatek\system\mssi_64_cn\SystemConfig.mk 文件下,配置BUILD_GMS = yes
第三方Launcher需要默认为Launcher时候-系统Launcher3 的配置
实际工控产品或者消费产品非GMS产品中,客户自定义的Launcher 软件,那么如何配置默认的Launcherne ?
一般有两种解决方案:
- 系统默认Launcher存在,在源码Framework层默认第三方Launcher为系统Launcher
- 系统原生Launcher3 也编译到系统里面去,但是去掉Home 属性。
如下:
参考资料
Android11.0内置第三方Launcher并设置为默认,保留Launcher3并可切换
设置三方应用为默认Launcher
动态设置默认Launcher
android 13.0-launcher3中workspaceapp列表页不显示某个app图标
Android13设置默认Launcher分析
其它相关Launcher 资料相关参考:
Launcher3 相关资料参考
菜鸟成长之路-源码分析专栏
Android Launcher3 简介
Launcher3 高端定制
Launcher3 开发
Launcher3 Android Code Search在线源码查看
Launcher3 xref 在线源码查看
Launcher3 RK 源码查看
Launcher3 解析
Launcher3 AndroidP AS版本
谷歌Launcher3 Android13源码修改
Launcher3 和 Launcher3QuickStep 区别
Android14 不分Launcher3修改
Launcher3 LoaderTask 的数据加载
Android14 浅析Launcher
Android O Launcher3-Workspace加载
- 上面参考资料都是两种Launcher情况下默认其中一个Launcher 作为默认Launcher; 我们上面分析的是保证一个Launcher 即可,系统Launcher 其实去掉了HOME属性。
- 系统Launcher3 开发,强烈建议多了解下Launcher3 相关的基础知识,数据架子-布局加载-框架相关基本内容,方便分析代码、实现功能,有相关思路。
实现方案
源码分析
Launcher
在onCreate 方法中,有下面这段代码,见名知意 module 相关
private LauncherModel mModel;
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
...
mModel = app.getModel();
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
}
}
...
}
LauncherModel
addCallbacksAndLoad 方法
/**
* Adds a callbacks to receive model updates
* @return true if workspace load was performed synchronously
*/
public boolean addCallbacksAndLoad(Callbacks callbacks) {
synchronized (mLock) {
addCallbacks(callbacks);
return startLoader(new Callbacks[] { callbacks });
}
}
startLoader
看这个方法里面的注释:
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
private boolean startLoader(Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// If there is already one running, tell it to stop.
boolean wasRunning = stopLoader();
boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
if (callbacksList.length > 0) {
// Clear any pending bind-runnables from the synchronized load process.
for (Callbacks cb : callbacksList) {
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
loaderResults.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
return true;
} else {
stopLoader();
// 数据加载 调用工作线程,规避主线程阻塞
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
return false;
}
这里找到相关的加载App 位置了:
loaderResults.bindWorkspace(bindAllCallbacks);
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
LoaderResults
源码如下: 它是一个中间方法,暂未实质性功能,应该后面方便系统扩展和用户自己扩展了
/**
* Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
*/
public class LoaderResults extends BaseLoaderResults {
public LoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
@Override
public void bindDeepShortcuts() {
}
@Override
public void bindWidgets() {
}
}
BaseLoaderResults
我们看看这个类的定义:
/**
* Stores the list of all applications for the all apps view.
*/
public class AllAppsList {
存储app 界面所有应用的集合。
bindAllApps
public void bindAllApps() {
// shallow copy
AppInfo[] apps = mBgAllAppsList.copyData();
int flags = mBgAllAppsList.getFlags();
executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}
这里看到 AppInfo[] apps = mBgAllAppsList.copyData();,下面看到的 bindAllApplications 绑定所有app ,我们最好从顶层来处理拦截 部分不想显示的应用
那就看一下 mBgAllAppsList 在哪里定义的:
private final AllAppsList mBgAllAppsList;
public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
。。。。
mBgAllAppsList = allAppsList;
。。。。
}
所以继续找到 它的子类,上面我们说了它的子类LoadResults 只是一个中间类,再次贴出它的部分代码:
public class LoaderResults extends BaseLoaderResults {
public LoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
那么我们再次回到如上 LauncherModel 的startLoader 方法
private boolean startLoader(Callbacks[] newCallbacks) {
........................
synchronized (mLock) {
// If there is already one running, tell it to stop.
boolean wasRunning = stopLoader();
boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
if (callbacksList.length > 0) {
...............
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
loaderResults.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
return true;
} else {
stopLoader();
// 数据加载 调用工作线程,规避主线程阻塞
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
return false;
}
那就继续追 LoaderResults 构造方法里面的第三个参数 mBgAllAppsList
找到变量定义如下:
// < only access in worker thread >
private final AllAppsList mBgAllAppsList;
找到它初始化地方:
LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter,
boolean isPrimaryInstance) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
isPrimaryInstance);
}
那么接下来要分析的就是 AllAppsList
AllAppsList
如上在分析 BaseLoaderResults 类的时候 ,已经分析到了 AllAppsList ,然后在bindAllApps 方法里面找到了 mBgAllAppsList.copyData(); ,最后反推到 AllAppsList , 这里需要分析的不就是 如上提到的 copyData嘛。
public AppInfo[] copyData() {
AppInfo[] result = data.toArray(EMPTY_ARRAY);
Arrays.sort(result, COMPONENT_KEY_COMPARATOR);
return result;
}
返回的是AppInfo 数组,那么我们过滤掉 我们不让显示的 App 不就解决了需求了嘛。 先看看 AppInfo
如类注释说明:它代表的就是一个展示在所有AppView 中的一个App . 我们可以通过它的属性类ComponentName 的包名来过滤掉我们想要隐藏的App
修改实现-举例说明
在第一个内容模块,已经说明了修改路径和修改方法,里面其实已经举例了修改的隐藏文件App
修改前:
修改之后,编译固件,开机后查看日志如下:说明修改已经起作用了。
最后看实际界面效果:可以看到文件app 已经被隐藏了,没有显示出来,但是这个app 是安装成功了的。
去系统设置看看,有木有这个app:看到是有这个app,已经安装过了的。 说明 隐藏成功了…
总结
- 这个需求解决方案,完全根据代码找到并进行修改实现,思路还是很清晰的
- 建议多看看Launcher 相关基础知识,对业务和流程分析非常有好处的。