update launcher code

Signed-off-by: LiuZhenyu2021 <liuzhenyu.liuzhenyu@huawei.com>
This commit is contained in:
LiuZhenyu2021 2021-09-15 21:41:07 +08:00
parent 7dec41e250
commit c39b9a0288
228 changed files with 6272 additions and 8125 deletions

View File

@ -1,42 +1,584 @@
# 桌面<a name="ZH-CN_TOPIC_0000001103554544"></a>
- [简介](#section11660541593)
- [架构图](#section78574815486)
- [目录](#section161941989596)
- [相关仓](#section1371113476307)
## 简介<a name="section11660541593"></a>
桌面作为系统人机交互的首要入口,提供应用图标的显示、点击启动、卸载应用,以及桌面布局设置、最近任务管理等功能。
### 架构图<a name="section78574815486"></a>
![](figures/launcherl2-en.png)
## 目录<a name="section161941989596"></a>
# Launcher 源码开发说明
## 1. 项目介绍
Launcher 作为系统人机交互的首要入口,提供应用图标的显示、点击启动、卸载应用,并提供桌面布局设置以及最近任务管理等功能。
Launcher 采用纯 JS 语言开发,开发过程中不涉及任何 Java 部分的代码。
## 2. 工程结构
### 目录结构
```
/applications/standard/launcher/
├── figures # 架构图目录
├── launcher # 桌面主Ability提供应用图标的显示、点击启动、卸载应用、桌面布局设置
├── launcher # 主launcher模块目录
│ └── src
│ └── main
│ ├── config.json # 全局配置文件
│ ├── js # js代码目录
│ └── resources # 资源配置文件存放目录
├── recents # 最近任务Ability提供最近任务管理
│ ├── js # JS代码目录
│ ├── default
│ ├── common # JS代码目录
│ ├── cache # 缓存目录
│ ├── colors # 颜色目录
│ ├── component # 自定义组件目录
│ ├── configs # 应用配置对象目录
│ ├── constants # 应用常量对象目录
│ ├── css # 样式目录
│ ├── model # Model层代码目录
│ ├── pics # 应用图片目录
│ ├── utils # 工具类代码目录
│ ├── i18n # 国际化目录
│ ├── pages # 业务特性的View层目录
│ ├── AppGridView # 图标网格的View目录
│ ├── AppListView # 图标列表的View目录
│ ├── EntryView # 应用入口的View目录
│ ├── presenter # presenter层代码目录
│ ├── app # 应用管理业务代码目录
│ ├── entry # 应用入口业务代码目录
│ ├── resources # 资源目录
│ ├── config.json # 项目配置信息
├── recents # 最近任务模块目录
│ └── src
│ └── main
│ ├── config.json # 全局配置文件
│ ├── js # js代码目录
│ └── resources # 资源配置文件存放目录
├── signature # 证书文件目录
├── LICENSE # 许可文件
│ ├── js # JS代码目录
│ ├── default
│ ├── common # JS代码目录
│ ├── component # 自定义组件目录
│ ├── css # 样式目录
│ ├── model # Model层代码目录
│ ├── pics # 应用图片目录
│ ├── i18n # 国际化目录
│ ├── pages # 业务特性的View层目录
│ ├── recent # 最近任务的View目录
│ ├── presenter # presenter层代码目录
│ ├── recent # 最近任务业务代码目录
│ ├── resources # 最近任务模块资源目录
│ ├── config.json # 项目配置信息
├── settings # 桌面设置模块目录
│ └── src
│ └── main
│ ├── js # JS代码目录
│ ├── default
│ ├── common # JS代码目录
│ ├── configs # 应用配置对象目录
│ ├── constants # 应用常量对象目录
│ ├── model # Model层代码目录
│ ├── pics # 应用图片目录
│ ├── i18n # 国际化目录
│ ├── pages # 业务特性的View层目录
│ ├── settings # 最近任务的View目录
│ ├── presenter # presenter层代码目录
│ ├── settings # 最近任务业务代码目录
│ ├── resources # 最近任务模块资源目录
│ ├── config.json # 项目配置信息
```
### 整体架构
![](./figures/launcherl2-en.png)
Launcher 整体以 OpenHarmony 既有的 MVVM 的 App 架构设计为基础,向下扩展出一套 MVPView, Presenter, Model分层架构既有的 MVVM 框架整体理解为新扩展的 MVP 框架的 View 层),用于处理 Launcher 的业务逻辑与数据管理。
应用整体采用了多模块的设计方式,每个模块都遵循上述架构原则。
各层的作用分别如下:
- 视图层View负责更新 UI 显示以及触摸与点击事件的监听。
- 展现层Presenter负责处理视图层View发送的业务逻辑处理请求并连通 Model 层获取数据。
- 模型层Model负责处理展现层Presenter 中关于数据处理的请求以及返回数据请求结果。
应用各层中重要类及其功能如下表所示
|模块|层级|类名|作用|
|-|-|-|-|
|launcher|视图层|EntryView|桌面入口画面的视图层逻辑控制类。|
|launcher|视图层|AppGridView|桌面网格视图画面的视图层逻辑控制类。|
|launcher|视图层|AppListView|桌面列表视图画面的视图层逻辑控制类。|
|launcher|展现层|EntryPresenter|桌面入口画面的展现层业务逻辑类,主要职责是判断桌面类型,以决定展现网格视图或者列表视图。|
|launcher|展现层|AppGridPresenter|桌面网格布局的展现层业务逻辑类,主要职责是接收视图层请求,处理逻辑业务,业务逻辑处理后实现功能或者向模型层发送请求。|
|launcher|展现层|AppListPresenter|桌面列表布局的展现层业务逻辑类,主要职责是接收视图层请求,处理逻辑业务,业务逻辑处理后实现功能或者向模型层发送请求。|
|launcher|模型层|AppModel|接收展现层AppGridPresenter和AppListPresenter发送过来的请求为其提供所需要的数据或实现其请求的功能。|
|launcher|模型层|SettingsModel|接收展现层EntryPresenter和SettingsPresenter发送过来的请求为其提供所需要的数据或实现其请求的功能。|
|recent|视图层|RecentsView|最近任务列表视图画面的视图层逻辑控制类。|
|recent|展现层|RecentsPresenter|最近任务列表的展现层业务逻辑类,主要职责是接收视图层请求,处理逻辑业务,业务逻辑处理后实现功能或者向模型层发送请求。|
|recent|模型层|RecentsModel|接收展现层RecentsPresenter发送过来的请求为其提供所需要的数据或实现其请求的功能。|
|settings|视图层|SettingsView|桌面设置画面的视图层层逻辑控制类。|
|settings|展现层|SettingsPresenter|设置桌面画面的业务逻辑类,主要职责是处理来自视图层的请求,业务逻辑处理后实现功能或者向模型层发送请求。|
|settings|模型层|SettingsModel|接收展现层EntryPresenter和SettingsPresenter发送过来的请求为其提供所需要的数据或实现其请求的功能。|
## 3. 代码使用
### 代码下载
基于L2的 Launcher 代码可以采用从码云上克隆的方式下载。
[下载地址](https://gitee.com/openharmony/applications_launcher)
#### 从码云克隆代码
##### 1.配置SSH公钥
1. 通过[登录网址](https://gitee.com/login)登录码云
2. 在码云的个人设置中设置SSH公钥
1. 生成SSH公私钥
在命令窗口输入以下命令,邮箱地址替换为自己的邮箱地址,一直回车直至完成为止。
```
ssh-keygen -t rsa -C "xxxx@xxxx.com"
```
> 执行完成后,会在用户目录下的.ssh文件夹内生成 `id_rsa``id_rsa.pub` 两个文件,其中的 `id_rsa.pub` 即为生成的 SSH公钥
> ![](./figures/git_ssh_pub_key.png)
2. 复制公钥内容
输入`cat .ssh/id_rsa.pub`,复制打印内容。
![](./figures/git_ssh_pub_key_content.png)
3. 在设置->安全设置->SSH公钥 中设置 SSH公钥
将第二步中复制的内容按照画面提示粘贴到码云上相应的内容区域,并点击确定。
![](./figures/gitee_ssh_key_setting.png)
> 注意在码云中添加SSH公钥之后需要进行账号的密码验证验证通过才能成功添加。
##### 2.下载代码
1. 浏览器打开[下载地址](https://gitee.com/openharmony/applications_launcher)。
2. 点击“克隆/下载”按钮,选择 SSH点击“复制”按钮。
![](./figures/source_download_1.png)
3. 在本地新建 Launcher 目录,在 Launcher 目录中执行如下命令
```
git clone 步骤2中复制的地址
```
![](./figures/git_clone_done.png)
### 环境搭建
#### 1. 下载安装 DevEco Studio
Launcher 使用 DevEco Studio 进行开发,开发前需要下载 DevEco Studio 。
**下载**
在下载页面下载 DevEco Studio 的安装包压缩文件([下载地址](https://developer.harmonyos.com/cn/develop/deveco-studio#download))。
下载页面如下:
![](./figures/ds_download.png)
> 注:下载 DevEco Studio 需要注册华为账号。
> ![](figures/hw_register.png)
**安装**
将下载下来的压缩包解压,得到安装文件如图:
![](./figures/ds_exe.png)
双击安装文件进行安装,安装过程如图所示:
![](./figures/ds_install_1.png)
![](./figures/ds_install_2.png)
#### 2. 安装开发 SDK
DevEco Studio 在安装完成之后会自动下载开发 SDK 下载过程如下图所示:
![](./figures/sdk_downloading.png)
File → Open 选择工程目录,点击 OK。
![](./figures/ds_import_project.png)
![](./figures/ds_import_project_ok.png)
选择Build → Build Haps(s)/APP(s) → Build Hap(s)
![](./figures/ds_build_haps.png)
## 4. 基础开发说明
### 异步回调的使用
在JS工程中异步回调的使用是一个非常常见的编码需求在 OpenHarmony 的开发中也不例外,这里以获取应用列表信息为例,演示如何在 OpenHarmony 的应用开发中使用回调方法。
假设我们的系统结构为MVP现在要在Presenter`AppPresenter.js`层获取Model`AppModel.js`层提供的应用列表数据并返回给View`AppView.js`)层中使用。
1. 在 `AppModel.js` 中添加获取应用信息的方法。
```JS
import BundleMgr from '@ohos.bundle';
export default class AppModel {
// 获取应用信息
getApplicationInfos(callback) {
// callback 为传入的异步回调方法
BundleMgr.getBundleInfos().then(data => {
// 在获取数据成功后,执行回调方法
callback(data);
});
}
}
```
## 相关仓<a name="section1371113476307"></a>
2. 在 `AppPresenter.js` 中定义供 View 层获取数据的方法 `getApplicationInfos(callback)`
```JS
export default class AppPresenter {
constructor(AppModel) {
this.appModel = AppModel;
}
// 获取应用信息
getApplicationInfos(callback) {
appModel.getApplicationInfos(callback);
}
}
```
3. 在 `AppView.js` 中定义获取数据的回调方法 `applicationInfosCallback(data)` (即上文中的 `AppModel.getApplicationInfos(callback)` 中的 `callback` 参数),并将其绑定到 `AppPresenter.getApplicationInfos(callback)``callback` 参数。
```JS
import AppPresenter from './AppListPresenter.js';
var appPresenter;
export default {
data: {
appList: []
},
onInit() {
appPresenter = new AppPresenter(this.$app.$def.data.appModel);
// 绑定回调方法
appPresenter.getApplicationInfos(this.applicationInfosCallback.bind(this))
},
// 定义获取数据的回调方法
applicationInfosCallback(data) {
this.appList = data;
}
}
```
这样,在 `AppModel.js` 中获取数据的方法 `getApplicationInfos` 异步执行成功后,就会将数据通过我们绑定的回调方法,回传到 `AppView.js` 中。
### 多语言支持
#### 定义资源文件
- 在 `src/main/js/default/i18n/`目录下,根据不同的语言定义 `.json` 资源文件。
![](./figures/ds_i18n_files.png)
#### 引用资源
- 在有对应page的js文件中可直接通过`$t()`引用。
```` JavaScript
this.title = this.$t('strings.world');
````
- 在没有对应page的js文件中需要参考如下代码通过有对应page的js文件引用。
```` JavaScript
// 在page页面中定义 globalThis.$globalT
globalThis.$globalT =null;
export default {
data: {
title: 'World'
},
onInit(){
globalThis.$globalT =this.$t.bind(this);
}
}
````
```` JavaScript
// 在非page页面使用方式
globalThis.$globalT =null;
export default class TestClass {
constructor{
this.title = globalThis.$globalT('strings.world');
}
}
````
## 5. 典型接口的使用
1. 启动 Ability
```JS
import FeatureAbility from '@ohos.ability.featureability';
//参数paramAbilityname, paramBundleName 是对应应用的abilitynamebundleName
startAbility(paramAbilityname, paramBundleName) {
FeatureAbility.startAbility({
bundleName: paramBundleName,
abilityName: paramAbilityname,
requestCode: 1,
abilityType: "PageAbility",
want:{
action: "action1",
entities: ["entity1"],
type: "PageAbility",
flags: 2,
elementName: {
deviceId : "deviceId",
bundleName : paramBundleName,
abilityName : paramAbilityname,
},
},
syncOption: 1,
}).then(data =>
console.info("Launcher promise::then : " + JSON.stringify(data))
).catch(error =>
console.info("Launcher promise::catch : " +JSON.stringify(error) )
);
}
```
2. 查询应用 bundle 信息
```JS
import BundleMgr from '@ohos.bundle';
//参数bundleName 是对应应用的bundleName
getBundleInfo(bundleName) {
BundleMgr.getBundleInfo(bundleName).then(data => {
console.info('Launcher getBundleInfo ' + data);
});
}
```
3. 查询所有应用信息
```JS
import BundleMgr from '@ohos.bundle';
getApplicationInfos() {
BundleMgr.getBundleInfos().then((data) => {
console.info('Launcher getApplicationInfos '+JSON.stringify(data));
});
}
```
4. 卸载应用
```JS
import BundleMgr from '@ohos.bundle';
//参数bundleName 是对应应用的bundleName
uninstallApp(bundleName) {
var result = BundleMgr.getBundleInstaller().then((data) =>{
data.uninstall(bundleName,{
param: {
userId: 0,
isKeepData: false
}
})
console.info("Launcher uninstall data [" + JSON.stringfy(data) + "]");
}).catch(error => console.info("Launcher uninstall err " + error));
}
```
5. Preferences 存取数据
```JS
import storage from '@ohos.data.storage';
//保存数据的落盘位置
const PREFERENCES_PATH = '/data/accounts/account_0/appdata/com.ohos.launcher/sharedPreference/LauncherPreference';
//键值对标识key类似localStorage用法
const PREFERENCES_KEY = 'PREFERENCES_KEY';
save(data) {
mPreferences.putSync(PREFERENCES_KEY, data);
mPreferences.flushSync();
}
get() {
var data = mPreferences.getSync(PREFERENCES_KEY, "defaultValue");
return data;
}
```
6. 获取最近任务列表
```JS
import NapiAbilityManager from '@ohos.app.abilitymanager';
getRecentMissions() {
NapiAbilityManager.queryRunningAbilityMissionInfos().then((data) => {
console.info("Launcher getRecentMissions data [" + data + "]");
});
}
```
7. 移除指定任务
```JS
import NapiAbilityManager from '@ohos.app.abilitymanager';
//参数missionId是所选择的最近任务的missionId
removeMission() {
NapiAbilityManager.removeMission(missionId).then((data) => {
console.info('Launcher removeMission data [' + data + ']');
});
}
```
## 6. 签名打包
### 签名
#### 签名文件的获取
1. 拷贝 OpenHarmony 标准版的 prebuilts\signcenter 目录到操作目录。
2. 拷贝Launcher工程的 signature\launcher.p7b 到该目录下。
#### 签名文件的配置
打开项目工程,选择 File → Project Structure
![](./figures/signature_1.png)
选择 Modules → Signing Configs将对应的签名文件配置如下完成后点击Apply再点击OK。
![](./figures/signature_2.png)
配置完成后对应的build.gradle文件中会出现如下内容
![](./figures/signature_3.png)
### 打包
DevEco Studio 支持 debug 与 release 两种打包类型。可以在 OhosBuild Variants 窗口中进行切换。
![](./figures/ds_ohosbuild_variants.png)
#### debug打包
1. 代码准备完成后,在 OhosBuild Variants 窗口的 Selected Variant 中选择 debug。
![](./figures/ds_ohosbuild_variants_debug.png)
2. 选择Build → Build Haps(s)/APP(s) → Build Hap(s)
![](./figures/ds_build_haps.png)
3. 编译完成后hap包会生成在工程目录下的 `\build\outputs\hap\debug\phone\`路径下如果没有配置签名则只会生成未签名的hap包
![](./figures/ds_ohosbuild_output_dir_debug.png)
#### release打包
1. 代码准备完成后,在 OhosBuild Variants 窗口的 Selected Variant 中选择 release
![](./figures/ds_ohosbuild_variants_release.png)
2. 选择Build → Build Haps(s)/APP(s) → Build Hap(s)
![](./figures/ds_build_haps.png)
3. 编译完成后hap包会生成在工程目录下的 `\build\outputs\hap\debug\phone\`路径下如果没有配置签名则只会生成未签名的hap包
![](./figures/ds_ohosbuild_output_dir_debug.png)
## 7. 安装、运行、调试
### 应用安装
配置 hdc
进入SDK目录中的toolchains文件夹下获取文件路径
![](./figures/screenshot-20210521-105407.png)
并将此路径配置到环境变量中:
![](./figures/screenshot-20210521-111223.png)
重启电脑使环境变量生效
***T.B.D 是否真的需要重启电脑?***
连接开发板打开cmd命令窗口执行`hdc list targets`,弹出窗口如下:
![](./figures/cmd1.png)
等待一段时间后,窗口出现如下打印,可回到输入 hdc list targets 的命令窗口继续操作:
![](./figures/cmd2.png)
再次输入hdc list targets出现如下结果说明hdc连接成功
![](./figures/cmd3.png)
获取 root 权限与读写权限:
```
hdc smode
hdc target mount
```
> hdc smode 命令的作用是获取 root 权限,但是该命令在当前版本下可能仍不稳定,执行此命令可能导致后续的 hdc target mount 失败,在这里为可选命令
***T.B.D smode 不执行的情况下hdc target mount 能否正常执行?***
将签名好的 hap 包放入设备的 `/system/app` 目录下并修改hap包的权限。发送文件命令如下
```
hdc file send 本地路径 /system/app/hap包名称
```
例:将当前本地目录的 `Launcher.hap` 文件放入到 `system/app/Launcher.hap` 文件中。
```
hdc file send Launcher.hap /system/app/Launcher.hap
```
> 注意,如果设备不存在 `/system/app` 目录,则需要手动创建该目录并修改权限。
> ```
> hdc shell
> cd system
> mkdir app
> chmod 777 app
> ```
> `/system/app` 目录放置系统应用例如LauncherSystemUISettings 等。
>
> 但hap包需要在该目录下手动设置权限
> ```
> chmod 666 hap包名
> ```
> 此目录应用不用手动安装,系统自动拉起。
### 应用运行
Launcher 属于系统应用,在将签名的 hap 包放入 `/system/app` 目录后,重启系统,应用会自动拉起。
```
hdc shell
reboot
不可以直接执行hdc reboot命令是无效的)
```
***T.B.D 直接执行 `reboot`,还是执行`hdc shell reboot`***
> 注意,如果设备之前安装过系统应用,则需要执行如下两条命令清除设备中存储的应用信息才能够在设备重启的时候将我们装入设备的新 hap 包正常拉起。
> ```
> hdc shell rm -rf /data/accounts/
> hdc shell rm -rf /data/misc_de/0/mdds/0/default/bundle_manager_service
> ```
### 应用调试
#### log打印
- 在程序中添加 log
```JS
console.info("Launcher log info");
```
可以在DevEco Studio中查看log
![](./figures/ds_hilog_window.png)
#### log获取及过滤
- log获取
将log输出至文件
```
hdc shell hilog > 输出文件名称
```
例:
在真实环境查看log将全log输出到当前目录的hilog.log文件中
```
hdc shell hilog > hilog.log
```
- log过滤
在命令行窗口中过滤log
```
hilog | grep 过滤信息
```
例:过滤包含信息 Label 的 hilog
```
hilog | grep Label
```
## 8. 贡献代码
### Fork 代码仓库
1. 在码云上打开 Launcher 代码仓库([仓库地址](https://gitee.com/openharmony/applications_launcher))。
2. 点击仓库右上角的 Forked 按钮
![](./figures/commit_source_fork_button.png)
3. 在弹出的画面中,选择将仓库 fork 到哪里,点击确认。
![](./figures/commit_source_fork_confirm.png)
4. Fork 成功之后,会在自己的账号下看见 fork 的代码仓库。
![](img/commit_source_forked_repo.png)
### 提交代码
1. 访问我们自己在码云账号上 fork 的代码仓库,点击“克隆/下载”按钮,选择 SSH点击“复制”按钮。
![](img/commit_source_clone_page.png)
2. 在本地新建 Launcher 目录,在 Launcher 目录中执行如下命令
```
git clone 步骤1中复制的地址
```
3. 修改代码。
> 将代码引入工程,以及编译工程等相关内容请参见 **3. 代码使用** 部分的相关内容。
4. 提交代码到 fork 仓库。
> 修改后的代码,首先执行 `git add` 命令,然后执行 `git commit` 命令与 `git push` 命令,将代码 push 到我们自己的 fork 仓中。
> 关于代码提交的这部分内容涉及 git 的使用,可以参照 [git官网](https://git-scm.com/) 的内容,在此不再赘述。
### 发起 Pull Request (PR)
在将代码提交到 fork 仓之后,我们可以通过发起 Pull RequestPR的方式来为 OpenHarmony 的相关项目贡献代码。
1. 打开 fork 仓库。选择 `Pull Requests``新建 Pull Request`
![](./figures/commit_source_new_pull_request.png)
2. 在 `新建 Pull Request` 画面填入标题与说明,点击 `创建` 按钮。
![](./figures/commit_source_new_pull_request_confirm.png)
3. 创建 Pull Request 完成。 PR 创建完成后,会有专门的代码审查人员对代码进行评审,评审通过之后会合入相应的代码库。
![](./figures/commit_source_new_pr_done.png)
系统应用
**applications\_standard\_launcher**

View File

@ -22,7 +22,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.huawei.ohos:hap:2.4.4.3-RC'
classpath 'com.huawei.ohos:hap:3.0.1.6'
}
}

21
common/build.gradle Normal file
View File

@ -0,0 +1,21 @@
apply plugin: 'com.huawei.ohos.library'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 6
defaultConfig {
compatibleSdkVersion 4
}
supportSystem "standard"
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
entryModules "phone"
}
dependencies {
}

View File

@ -0,0 +1,22 @@
{
"app": {
"bundleName": "com.ohos.launcher",
"vendor": "ohos",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"deviceConfig": {},
"module": {
"package": "com.ohos.launcher",
"deviceType": [
"phone"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "common",
"moduleType": "har"
}
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import AppModel from '../common/model/AppModel.ets'
import SettingsModel from '../common/model/SettingsModel.ets'
import AppListInfoCacheManager from '../cache/AppListInfoCacheManager.ets'
import ResourceManager from '../manager/ResourceManager.ets'
const KEY_APP_LIST = "appListInfo";
const UNINSTALL_SUCCESS = true;
const KEY_NAME = "name";
const UNINSTALL_PROHIBITED = "UNINSTALL_PROHIBITED";
const SETTING_BUNDLE = 'com.ohos.launcher';
const SETTING_ABILITY = 'com.ohos.launcher.settings.MainAbility';
export default class BaseAppPresenter {
protected mAppModel: AppModel;
protected mSettingsModel: SettingsModel;
protected mAppListInfoCacheManager: AppListInfoCacheManager;
protected mResourceManager: ResourceManager;
protected mScreenHeight: number = 0;
protected mScreenWidth: number = 0;
private mAppListInstallListener;
private mAppListUninstallListener;
private mAppListChangeListener;
protected constructor() {
this.mAppModel = AppModel.getInstance();
this.mSettingsModel = new SettingsModel();
this.mAppListInfoCacheManager = new AppListInfoCacheManager();
this.mResourceManager = ResourceManager.getInstance();
this.mAppListInstallListener = this.appListInstallListener.bind(this);
this.mAppListUninstallListener = this.appListUninstallListener.bind(this);
this.mAppListChangeListener = this.appListChangeListener.bind(this);
}
public setScreenHeight(height: number): void{
this.mScreenHeight = height;
}
public setScreenWidth(width: number): void{
this.mScreenWidth = width;
}
jumpTo(abilityName, bundleName) {
this.mAppModel.startApplication(abilityName, bundleName);
}
jumpToSetting() {
this.mAppModel.startApplication(SETTING_ABILITY, SETTING_BUNDLE);
}
uninstallApp(uninstallBundleName, ifSystem) {
if (ifSystem == UNINSTALL_SUCCESS) {
this.regroupDataAfterUnInstall(UNINSTALL_PROHIBITED);
} else {
this.mAppModel.uninstallApp(uninstallBundleName, this.uninstallAppCallback.bind(this));
}
}
uninstallAppCallback(callback) {
this.regroupDataAfterUnInstall(callback);
}
registerAppListChangeCallback() {
this.mAppModel.registerAppListInstallListener(this.mAppListInstallListener);
this.mAppModel.registerAppListUninstallListener(this.mAppListUninstallListener);
this.mAppModel.registerAppListChangeListener(this.mAppListChangeListener);
}
unregisterAppListChangeCallback() {
console.info("Launcher appPresenter unregisterAppListChangeCallback");
this.mAppModel.unregisterAppListInstallListener(this.mAppListInstallListener);
this.mAppModel.unregisterAppListUninstallListener(this.mAppListUninstallListener);
this.mAppModel.unregisterAppListChangeListener(this.mAppListChangeListener);
}
public appListInstallListener(bundleInfo) {
let nameCallback = (appName) => {
bundleInfo.AppName = appName;
this.modifyItemList(this.appendItem, bundleInfo);
}
this.mResourceManager.getAppName(bundleInfo.labelId, bundleInfo.bundleName, bundleInfo.AppName, nameCallback);
}
private appendItem(list, item) {
list.push(item);
return list;
}
public appListUninstallListener(bundleInfo) {
this.modifyItemList(this.removeItem, bundleInfo);
}
private removeItem(list, item) {
for (let listItem of list) {
if (listItem.bundleName == item.bundleName) {
let index = list.indexOf(listItem);
list.splice(index, 1);
}
}
return list;
}
public appListChangeListener(bundleInfo) {
this.modifyItemList(this.replaceItem, bundleInfo);
}
private replaceItem(list, item) {
for (let listItem of list) {
console.info("Launcher replaceCache + " + listItem.bundleName + " + " + item.bundleName);
if (listItem.bundleName == item.bundleName) {
let index = list.indexOf(listItem);
list.splice(index, 1, item);
}
}
return list;
}
private modifyItemList(method, item) {
console.info("Launcher AppListPresenter appListChangeListener + " + JSON.stringify(item));
let currentCacheList = this.mAppListInfoCacheManager.getCache(KEY_APP_LIST);
method(currentCacheList, item);
this.regroupDataAfterInstall(currentCacheList);
}
public regroupDataAfterInstall(callbackList) {
}
public regroupDataAfterUnInstall(result) {
}
getAppName(keyName) {
return this.mResourceManager.getAppResourceCache(keyName, KEY_NAME);
}
}

View File

@ -13,55 +13,55 @@
* limitations under the License.
*/
import LruCache from './LruCache.js';
import DiskLruCache from './DiskLruCache.js';
import LruCache from './LruCache.ets';
import DiskLruCache from './DiskLruCache.ets';
/**
* A Manager class that provides get/set/clear cache methods for app list data.
*/
export default class AppListInfoCacheManager {
#lruCache;
#diskLruCache;
private lruCache;
private diskLruCache;
constructor() {
this.#lruCache = new LruCache();
this.#diskLruCache = new DiskLruCache();
}
constructor() {
this.lruCache = new LruCache();
this.diskLruCache = new DiskLruCache();
}
/**
/**
* Get cache from disk or memory.
*
* @param {string} key - key of the cache map
* @return {object} - cache get from the memory or disk
*/
getCache(key) {
console.info("Launcher AppListInfoCacheManager getCache key = " + key);
let cache = this.#lruCache.getCache(key);
if (cache == undefined || cache == null || cache == '' || cache == -1) {
return this.#diskLruCache.getCache(key);
} else {
return cache;
}
getCache(key) {
console.info("Launcher AppListInfoCacheManager getCache key = " + key);
let cache = this.lruCache.getCache(key);
if (cache == undefined || cache == null || cache == '' || cache == -1) {
return this.diskLruCache.getCache(key);
} else {
return cache;
}
}
/**
/**
* Set cache to disk or memory.
*
* @param {string} key - key of the cache map
* @param {object} value - value of the cache map
*/
setCache(key, value) {
console.info("Launcher AppListInfoCacheManager setCache key = " + key + " value = " + value);
this.#lruCache.putCache(key, value);
this.#diskLruCache.putCache(key, value);
}
setCache(key, value) {
console.info("Launcher AppListInfoCacheManager setCache key = " + key + " value = " + value);
this.lruCache.putCache(key, value);
this.diskLruCache.putCache(key, value);
}
/**
/**
* Clear cache of both disk and memory.
*/
clearCache() {
console.info("Launcher AppListInfoCacheManager clearCache");
this.#lruCache.clear();
this.#diskLruCache.clear();
}
clearCache() {
console.info("Launcher AppListInfoCacheManager clearCache");
this.lruCache.clear();
this.diskLruCache.clear();
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import LruCache from './LruCache.ets'
import DiskLruCache from './DiskLruCache.ets'
const KEY_ICON = "icon";
const DISK_CACHE_MISS = -1;
/**
* A Manager class that provides get/set/clear cache methods for app image data.
*/
export default class AppResourceCacheManager {
private memoryCache;
private diskCache;
constructor() {
this.memoryCache = new LruCache();
this.diskCache = new DiskLruCache();
}
/**
* Get cache from disk or memory.
*
* @param {string} key - key of the cache map
* @return {object} - cache get from memory or disk
*/
getCache(bundleName, key) {
console.info("Launcher AppResourceCacheManager getCache bundleName = " + bundleName + " key = " + key);
let cache = this.getCacheFromMemory(bundleName, key);
if (cache == undefined || cache == null || cache == '') {
if (key === KEY_ICON) {
return this.getCacheFromDisk(bundleName, key);
}
return null;
} else {
return cache;
}
}
/**
* Set cache to disk or memory.
*
* @param {string} key - key of the cache map
* @param {object} value - value of the cache map
*/
setCache(bundleName, key, value) {
console.info("Launcher AppResourceCacheManager setCache bundleName = " + bundleName + " key = " + key);
this.setCacheToMemory(bundleName, key, value);
if (key === KEY_ICON) {
this.setCacheToDisk(bundleName, key, value);
}
}
/**
* Clear cache of both disk and memory.
*/
clearCache() {
console.info("Launcher AppResourceCacheManager clearCache");
this.memoryCache.clear();
}
private getCacheFromMemory(bundleName, key) {
let cache = this.memoryCache.getCache(bundleName);
if (cache == undefined || cache == null || cache == '' || cache === -1) {
return null;
} else if (cache[key] == undefined || cache[key] == null || cache[key] == '') {
return null;
} else {
return cache[key];
}
}
private setCacheToMemory(bundleName, key, value) {
let cache = this.memoryCache.getCache(bundleName);
if (cache == undefined || cache == null || cache == '' || cache === -1) {
cache = {};
cache[key] = value;
} else {
cache[key] = value;
}
this.memoryCache.putCache(bundleName, cache);
}
private getCacheFromDisk(bundleName, key) {
let data = this.diskCache.getCache(bundleName);
return data !== DISK_CACHE_MISS ? data : null;
}
private setCacheToDisk(bundleName, key, value) {
this.diskCache.putCache(bundleName, value);
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import DiskLruFileUtils from "./DiskLruFileUtils.ets";
/**
* A class provides persistent operation for memory cache.
*/
export default class DiskLruCache {
private cache;
private capacity;
constructor(capacity = 100) {
this.cache = new Map();
this.capacity = capacity;
this.initMap(); //read cache from local
}
/**
* Init the cache whether the file has data.
*/
initMap() {
console.info("Launcher DiskLruCache initMap start execution");
try {
let arr = DiskLruFileUtils.readJournal().split("\n").reverse();
let len = arr.length >= this.capacity ? this.capacity : arr.length;
for (let i = 0;i < len; i++) {
this.cache.set(arr[i], arr[i]);
}
} catch (e) {
console.error("Launcher DiskLruCache initMap e " + e);
}
}
/**
* Get cache from disk.
*
* @param {string} key - key of the cache map
* @return {object} - target cache object
*/
getCache(key) {
if (this.cache.has(key)) {
// exist and update
let temp = this.cache.get(key);
//delete the old cache
this.cache.delete(key);
//update the cache to recent use
this.cache.set(key, temp);
//update local cache to recent use
DiskLruFileUtils.writeJournal(key);
return DiskLruFileUtils.readJsonObj(key)[key];
}
return -1;
}
/**
* Put cache to disk.
*
* @param {string} key - key of the cache map
* @param {object} value - value of the cache map
*/
putCache(key, value) {
if (this.cache.has(key)) {
// exist and update
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// if size > capacity ,remove the old
this.remove(this.cache.keys().next().value);
}
//update the cache to recent use
this.cache.set(key, value);
//update local cache to recent use
DiskLruFileUtils.writeJournal(key);
DiskLruFileUtils.writeJsonObj({
[key]: value
}, key);
}
/**
* Remove cache of corresponding key.
*
* @param {string} key - key of the cache map
*/
remove(key) {
this.cache.delete(key);
DiskLruFileUtils.removeFile(key);
}
/**
* Clear cache of disk.
*/
clear() {
this.cache.forEach(function (value, key) {
DiskLruFileUtils.removeFile(key);
});
this.cache.clear();
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Fileio from '@ohos.fileio';
const writeFilePath = "/data/accounts/account_0/appdata/com.ohos.launcher/cache/";
const journalPath = "/data/accounts/account_0/appdata/com.ohos.launcher/cache/journal.txt";
const READ_DATA_SIZE = 4096;
/**
* An util that provides io functionality which is used by DiskLruCache.
*/
export default class DiskLruFileUtils {
/**
* Read Json file from disk by bundleName.
*
* @param {string} bundleName - bundleName of the target file
* @return {object} read object from file
*/
static readJsonObj(bundleName) {
console.info("Launcher FileUtil readJsonObj start execution");
let filePath = writeFilePath + bundleName + ".json";
return this.readJsonFile(filePath);
}
/**
* Read Json file from disk by file path.
*
* @param {string} path - path of the target file.
* @return {object} read object from file
*/
static readJsonFile(path) {
console.info("Launcher FileUtil readJsonFile start execution");
let readStreamSync = null;
try {
readStreamSync = Fileio.createStreamSync(path, "r");
let content = this.getContent(readStreamSync);
console.info("Launcher FileUtil readJsonFile finish execution" + content);
return JSON.parse(content);
} catch (e) {
console.info("Launcher FileUtil readJsonFile " + e);
} finally {
readStreamSync.closeSync();
}
}
/**
* Write Json object to a file.
*
* @param {object} jsonObj - target JSON object will be written
* @param {string} bundleName - use bundleName as target file name
*/
static writeJsonObj(jsonObj, bundleName) {
console.info("Launcher FileUtil writeJsonObj start execution");
let filePath = writeFilePath + bundleName + ".json";
let content = JSON.stringify(jsonObj);
let writeStreamSync = null;
try {
writeStreamSync = Fileio.createStreamSync(filePath, "w+");
writeStreamSync.writeSync(content);
} catch (e) {
console.info("Launcher FileUtil writeJsonObj error: " + e);
} finally {
writeStreamSync.closeSync();
console.info("Launcher FileUtil writeJsonObj close sync");
}
}
/**
* Record a key that maps the image as value.
*
* @param {string} content - the key maps the image file
*/
static writeJournal(content) {
let writeStreamSync = null;
try {
console.info("Launcher FileUtil writeJournal start");
writeStreamSync = Fileio.createStreamSync(journalPath, "a+");
writeStreamSync.writeSync(content + "\n");
} catch (e) {
console.info("Launcher FileUtil writeJournal error: " + e);
} finally {
writeStreamSync.closeSync();
console.info("Launcher FileUtil writeJournal close sync");
}
}
/**
* Get the keys that map the images.
*
* @return {object} object read from file
*/
static readJournal() {
console.info("Launcher FileUtil readJournal start execution");
let readStreamSync = null;
try {
readStreamSync = Fileio.createStreamSync(journalPath, "r");
return this.getContent(readStreamSync);
} catch (e) {
console.info("Launcher FileUtil readJournal error: " + e);
} finally {
readStreamSync.closeSync();
console.info("Launcher FileUtil readJournal closeSync");
}
}
/**
* Read JSON object from a file.
*
* @param {object} readStreamSync - stream of target file
* @return {object} object read from file stream
*/
static getContent(readStreamSync) {
console.info("Launcher FileUtil getContent start");
let bufArray = [];
let totalLength = 0;
let buf = new ArrayBuffer(READ_DATA_SIZE);
let len = readStreamSync.readSync(buf);
while (len != 0) {
console.info("Launcher FileUtil getContent FileIO reading " + len);
totalLength += len;
if (len < READ_DATA_SIZE) {
buf = buf.slice(0, len);
bufArray.push(buf);
break;
}
bufArray.push(buf);
buf = new ArrayBuffer(READ_DATA_SIZE);
len = readStreamSync.readSync(buf);
}
console.info("Launcher FileUtil getContent read finished " + totalLength);
let contentBuf = new Uint8Array(totalLength);
let offset = 0;
for (let bufArr of bufArray) {
console.info("Launcher FileUtil getContent collecting " + offset);
let uInt8Arr = new Uint8Array(bufArr);
contentBuf.set(uInt8Arr, offset);
offset += uInt8Arr.byteLength;
}
let content = String.fromCharCode.apply(null, new Uint8Array(contentBuf));
return content;
}
/**
* Remove file.
*
* @param {string} bundleName - bundleName as target file name
*/
static removeFile(bundleName) {
try {
console.info("Launcher FileUtil removeFile")
//remove file,key : bundlename
Fileio.unlinkSync(writeFilePath + bundleName + ".json")
} catch (e) {
console.error("Launcher FileUtil removeFile delete has failed for " + e)
}
}
}

View File

@ -17,61 +17,64 @@
* A class provides memory cache operation.
*/
export default class LruCache {
constructor(capacity = 100) {
this.cache = new Map();
this.capacity = capacity; //the capacity of cache
}
private cache;
private capacity;
/**
constructor(capacity = 100) {
this.cache = new Map();
this.capacity = capacity; //the capacity of cache
}
/**
* Get cache from memory.
*
* @param {string} key - key of the cache map
* @return {object} - cache from memory
*/
getCache(key) {
if (this.cache.has(key)) {
// exist and update
let temp = this.cache.get(key);
//delete the old cache
this.cache.delete(key);
//update the cache to recent use
this.cache.set(key, temp);
return temp;
}
return -1;
getCache(key) {
if (this.cache.has(key)) {
// exist and update
let temp = this.cache.get(key);
//delete the old cache
this.cache.delete(key);
//update the cache to recent use
this.cache.set(key, temp);
return temp;
}
return -1;
}
/**
/**
* Put cache to disk.
*
* @param {string} key - key of the cache map
* @param {object} value - value of the cache map
*/
putCache(key, value) {
if (this.cache.has(key)) {
// exist and update
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// if size > capacity ,remove the old
this.cache.delete(this.cache.keys().next().value);
}
//update the cache to recent use
this.cache.set(key, value);
putCache(key, value) {
if (this.cache.has(key)) {
// exist and update
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// if size > capacity ,remove the old
this.cache.delete(this.cache.keys().next().value);
}
//update the cache to recent use
this.cache.set(key, value);
}
/**
/**
* Remove cache of corresponding key.
*
* @param {string} key - key of the cache map
*/
remove(key) {
this.cache.delete(key);
}
remove(key) {
this.cache.delete(key);
}
/**
/**
* Clear cache of memory.
*/
clear() {
this.cache.clear();
}
clear() {
this.cache.clear();
}
}

View File

@ -12,15 +12,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Default configuration of page layout and recent.
*/
const DefaultLayoutConfig = {
DefaultAppPageStartConfig: 'Grid',
DefaultGridConfig: 1,
DefaultRecentProcessLimit: 20,
DefaultRecentProcessLimitArray: [5,10,15,20],
DefaultAppPageStartConfig: 'Grid',
DefaultLayoutOptions: [{ name: 'List', value: 'List', checked: 'false' }, { name: 'Grid', value: 'Grid', checked: 'false' }],
DefaultGridConfig: 1,
DefaultRecentProcessLimit: 20,
DefaultRecentProcessLimitArray: [
{ name: '5', value: 5, checked: 'false' },
{ name: '10', value: 10, checked: 'false' },
{ name: '15', value: 15, checked: 'false' },
{ name: '20', value: 20, checked: 'false' }
],
}
export default DefaultLayoutConfig;

View File

@ -12,30 +12,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const GridLayoutConfigs = {
GridLayoutTable: [
{
id: 0,
layout: '4X4',
row: 4,
column: 4
},
GridLayoutTable: [
{
id: 0,
layout: '4X4',
row: 4,
column: 4,
checked: 'false'
},
{
id: 1,
layout: '5X4',
row: 5,
column: 4
},
{
id: 1,
layout: '5X4',
row: 5,
column: 4,
checked: 'false'
},
{
id: 2,
layout: '6X4',
row: 6,
column: 4
},
]
{
id: 2,
layout: '6X4',
row: 6,
column: 4,
checked: 'false'
},
]
}
export default GridLayoutConfigs;

View File

@ -12,10 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const PageData = {
GRID_APP_PAGE: 'pages/AppGridView/AppGridView',
LIST_APP_PAGE: 'pages/AppListView/AppListView'
const SystemApplication = {
SystemApplicationName: 'com.ohos.launcher,com.ohos.systemui,com.ohos.devicemanagerui'
}
export default PageData;
export default SystemApplication;

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default class StyleConstants {
public static DEFAULT_FONT_COLOR: string = '#ffffff'
public static DEFAULT_ICON: string = '/common/pics/icon.png'
public static DEFAULT_BACKGROUND_IMAGE: string = '/common/pics/img_wallpaper.jpg'
public static DEFAULT_APP_NAME_SIZE: number= 20;
public static DEFAULT_APP_ITEM_WIDTH: number = 70;
public static DEFAULT_APP_ITEM_HEIGHT: number = 95;
public static PERCENTAGE_100: string = '100%'
public static PERCENTAGE_85: string = '85%'
public static PERCENTAGE_15: string = '15%'
public static DEFAULT_APP_IMAGE:string = '/common/pics/img_app_default.png'
public static DEFAULT_DELETE_IMAGE:string = '/common/pics/ic_recent_delete.png'
public static DEFAULT_BACKGROUND_IMAGE_RECENT:string = '/common/pics/ic_wallpaper_recent.jpg'
public static APP_IMAGE_WIDTH: number = 240
public static APP_IMAGE_HEIGHT: number = 430
}

View File

@ -17,9 +17,9 @@
* Constants of events that will be registered to system.
*/
const EventConstants = {
EVENT_PACKAGE_ADDED : "usual.event.PACKAGE_ADDED",
EVENT_PACKAGE_CHANGED : "usual.event.PACKAGE_CHANGED",
EVENT_PACKAGE_REMOVED : "usual.event.PACKAGE_REMOVED"
EVENT_PACKAGE_ADDED: "usual.event.PACKAGE_ADDED",
EVENT_PACKAGE_CHANGED: "usual.event.PACKAGE_CHANGED",
EVENT_PACKAGE_REMOVED: "usual.event.PACKAGE_REMOVED"
}
export default EventConstants;

View File

@ -0,0 +1,269 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import BundleMgr from '@ohos.bundle';
import FeatureAbility from '@ohos.ability.featureability';
import Subscriber from '@ohos.commonevent';
import SystemApplication from '../configs/SystemApplication.ets';
import CheckEmptyUtils from '../../utils/CheckEmptyUtils.ets';
import EventConstants from '../constants/EventConstants.ets';
const UNINSTALL_SUCCESS = "UNINSTALL_SUCCESS";
const UNINSTALL_FAILED = "UNINSTALL_FAILED";
const IF_GET_ABILITY = 1;
export default class AppModel {
private static appModel = new AppModel();
private mBundleInfoList = [];
private systemApplicationName = SystemApplication.SystemApplicationName;
private mCommonEventSubscriber = null;
private mAppListInstallListener = [];
private mAppListUninstallListener = [];
private mAppListChangeListener = [];
private mUninstallCallback;
private mCommonEventSubscribeInfo = {
events: [
EventConstants.EVENT_PACKAGE_ADDED,
EventConstants.EVENT_PACKAGE_CHANGED,
EventConstants.EVENT_PACKAGE_REMOVED]
};
private constructor() {
}
public static getInstance(): AppModel{
return this.appModel;
}
getAppList(callback) {
this.mBundleInfoList = [];
console.info('Launcher AppModel getAppIcon getAppList');
BundleMgr.getBundleInfos(IF_GET_ABILITY)
.then((data) => {
if (CheckEmptyUtils.isEmpty(data)) {
console.error("Launcher AppModel getAppList getBundleInfos ERROR");
}
console.info('Launcher AppModel getBundleInfos >' + JSON.stringify(data));
for (let i = 0; i < data.length; i++) {
if (this.systemApplicationName.indexOf(data[i].name) > -1) {
} else {
this.mBundleInfoList.push(
{
System: data[i].appInfo.systemApp,
AppName: data[i].appInfo.label,
AppId: data[i].appId,
AppIcon: data[i].appInfo.iconId,
bundleName: data[i].name,
labelId: data[i].appInfo.labelId,
abilityName: data[i].abilityInfos[0].name,
}
)
}
}
let appArrayLength = this.mBundleInfoList.length;
console.info('Launcher AppModel mBundleInfoList' + JSON.stringify(this.mBundleInfoList));
callback(this.mBundleInfoList);
});
}
uninstallApp(uninstallBundleName, callback) {
console.info('Launcher AppModel uninstallApp appId' + uninstallBundleName);
this.mUninstallCallback = callback;
let result = BundleMgr.getBundleInstaller()
.then((data) => {
if (CheckEmptyUtils.isEmpty(data)) {
console.error("Launcher AppModel uninstallApp getBundleInstaller ERROR");
}
data.uninstall(uninstallBundleName, {
param: {
userId: 0,
isKeepData: false
}
}, this.uninstallCallback.bind(this));
})
.catch(error =>
console.info("Launcher AppModel uninstall err " + error));
}
private uninstallCallback(err, data) {
console.info('Launcher AppModel uninstallCallback ' + JSON.stringify(data));
if (data.statusMessage == "SUCCESS") {
this.mUninstallCallback(UNINSTALL_SUCCESS);
} else {
this.mUninstallCallback(UNINSTALL_FAILED);
}
console.info('Launcher AppModel uninstallCallback ');
}
registerAppListInstallListener(listener) {
if (this.mAppListInstallListener.indexOf(listener) == -1) {
this.mAppListInstallListener.push(listener);
}
}
/**
* Unregister install listener.
*
* @param {object} listener - install listener
*/
unregisterAppListInstallListener(listener) {
let index = this.mAppListInstallListener.indexOf(listener);
if (index != -1) {
this.mAppListInstallListener.splice(index, 1);
}
}
/**
* Register uninstall listener.
*
* @param {object} listener - uninstall listener
*/
registerAppListUninstallListener(listener) {
if (this.mAppListUninstallListener.indexOf(listener) == -1) {
this.mAppListUninstallListener.push(listener);
}
}
/**
* Unregister uninstall listener.
*
* @param {object} listener - uninstall listener
*/
unregisterAppListUninstallListener(listener) {
let index = this.mAppListUninstallListener.indexOf(listener);
if (index != -1) {
this.mAppListUninstallListener.splice(index, 1);
}
}
/**
* Register change listener.
*
* @param {object} listener - uninstall listener
*/
registerAppListChangeListener(listener) {
if (this.mAppListChangeListener.indexOf(listener) == -1) {
this.mAppListChangeListener.push(listener);
}
}
/**
* Unregister change listener.
*
* @param {object} listener - change listener
*/
unregisterAppListChangeListener(listener) {
let index = this.mAppListChangeListener.indexOf(listener);
if (index != -1) {
this.mAppListChangeListener.splice(index, 1);
}
}
registerAppListEvent() {
Subscriber.createSubscriber(
this.mCommonEventSubscribeInfo,
this.createInstallationSubscriberCallBack.bind(this)
);
}
unregisterAppListEvent() {
Subscriber.unsubscribe(this.mCommonEventSubscriber, () => {
console.info("Launcher AppModel unregisterAppListEvent");
});
}
private createInstallationSubscriberCallBack(err, data) {
console.info("Launcher AppModel createInstallationSubscriberCallBack");
this.mCommonEventSubscriber = data;
Subscriber.subscribe(this.mCommonEventSubscriber, this.installationSubscriberCallBack.bind(this));
}
private installationSubscriberCallBack(err, data) {
if (err.code == 0) {
if (CheckEmptyUtils.isEmpty(data)) {
console.error("Launcher AppModel installationSubscriberCallBack ERROR! data is empty");
}
console.info("Launcher AppModel installationSubscriberCallBack data = " + JSON.stringify(data));
let callbackData = data;
if (callbackData.event == EventConstants.EVENT_PACKAGE_REMOVED) {
this.reportAppInstallEvent(callbackData.event, callbackData);
return;
}
BundleMgr.getBundleInfo(callbackData.bundleName, IF_GET_ABILITY).then(data => {
console.info('Launcher AppModel installation subscriber getBundleInfo ' + JSON.stringify(data));
let bundleInfo = {
System: data.appInfo.systemApp,
AppName: data.appInfo.label,
AppId: data.appId,
AppIcon: data.appInfo.iconId,
bundleName: data.name,
labelId: data.appInfo.labelId,
abilityName: data.abilityInfos[0].name,
};
this.reportAppInstallEvent(callbackData.event, bundleInfo);
}).catch(e => {
console.error("Launcher AppModel installation subscriber getBundleInfo error ");
});
} else {
console.error("Launcher AppModel app list change failed --- err = " + JSON.stringify(err));
}
}
private reportAppInstallEvent(event, bundleInfo) {
console.info("Launcher AppModel reportAppInstallEvent + " + event);
switch (event) {
case EventConstants.EVENT_PACKAGE_ADDED:
this.notifyEventListener(this.mAppListInstallListener, bundleInfo);
break;
case EventConstants.EVENT_PACKAGE_CHANGED:
this.notifyEventListener(this.mAppListChangeListener, bundleInfo);
break;
case EventConstants.EVENT_PACKAGE_REMOVED:
this.notifyEventListener(this.mAppListUninstallListener, bundleInfo);
break;
default:
break;
}
}
startApplication(abilityName, bundleName) {
let paramBundleName = bundleName;
let paramAbilityName = abilityName;
// promise
console.info('Launcher ==> AppModel startApplication abilityName ==> ' + abilityName + " bundleName ==> " + bundleName);
let result = FeatureAbility.startAbility({
want: {
bundleName: paramBundleName,
abilityName: paramAbilityName
}
})
.then(data =>
console.info("Launcher AppModel startApplication promise::then : " + JSON.stringify(data))
)
.catch(error =>
console.info("Launcher AppModel startApplication promise::catch : " + JSON.stringify(error))
);
console.info("Launcher ==> AppModel startApplication AceApplication : startAbility : " + result);
}
private notifyEventListener(eventListener, bundleInfo) {
for (let i = 0; i < eventListener.length; i++) {
let listener = eventListener[i];
if (listener != undefined && listener != null) {
console.info("Launcher AppModel notifyEventListener " + JSON.stringify(bundleInfo));
listener(bundleInfo);
}
}
}
}

View File

@ -12,145 +12,138 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* An abstract class contains methods that can store layout data.
*/
export default class ILayoutConfig {
constructor() {
}
constructor() {
}
/**
/**
* Get the layout view type.
*
* @return {string} Layout view type, should one of 'Grid' or 'List' which is stored in LayoutConstants class.
*/
getAppPageStartConfig() {
return this.loadAppPageStartConfig();
}
public getAppPageStartConfig() {
return this.loadAppPageStartConfig();
}
/**
/**
* Set the layout view type.
*
* @param {string} type - Layout view type, should one of 'Grid' or 'List' which is stored in LayoutConstants class.
*/
setAppPageStartConfig(type) {
this.saveAppPageStartConfig(type);
}
public setAppPageStartConfig(type) {
this.saveAppPageStartConfig(type);
}
/**
/**
* Get grid layout config id.
*
* @return {number} grid layout config id.
*/
getGridConfig() {
return this.loadGridConfig();
}
public getGridConfig(): number {
return this.loadGridConfig();
}
/**
/**
* Set grid layout config id.
*
* @param {number} id - layout config id.
*/
setGridConfig(id) {
this.saveGridConfig(id);
}
public setGridConfig(id) {
this.saveGridConfig(id);
}
/**
/**
* Get recent process max limit.
*
* @return {number} recent process max limit.
*/
getRecentProcessLimit() {
return this.loadRecentProcessLimit();
}
public getRecentProcessLimit() {
return this.loadRecentProcessLimit();
}
/**
/**
* Set recent process max limit.
*
* @param {number} num - Recent process max limit.
*/
setRecentProcessLimit(num) {
this.saveRecentProcessLimit(num);
}
public setRecentProcessLimit(num) {
this.saveRecentProcessLimit(num);
}
/**
/**
* Get layout information of grid view.
*
* @return {object} layout information.
*/
getGridLayoutInfo() {
return this.loadGridLayoutInfo();
}
public getGridLayoutInfo() {
return this.loadGridLayoutInfo();
}
/**
/**
* Set layout information of grid view.
*/
setGridLayoutInfo(layoutInfo) {
this.saveGridLayoutInfo(layoutInfo);
}
public setGridLayoutInfo(layoutInfo) {
this.saveGridLayoutInfo(layoutInfo);
}
/**
/**
* Remove layout information of grid view.
*/
deleteGridLayoutInfo() {
this.removeGridLayoutInfo();
}
public deleteGridLayoutInfo() {
this.removeGridLayoutInfo();
}
/**
/**
* Should overridden by sub-classes , load the launcher layout view type.
*/
loadAppPageStartConfig() {
}
public loadAppPageStartConfig() {
}
/**
/**
* Should overridden by sub-classes , save the launcher layout view type.
*/
saveAppPageStartConfig(type) {
}
public saveAppPageStartConfig(type) {
}
/**
/**
* Should overridden by sub-classes , load the launcher grid view layout config id.
*/
loadGridConfig() {
}
public loadGridConfig(): any {
}
/**
/**
* Should overridden by sub-classes , save the launcher grid view layout config id.
*/
saveGridConfig(id) {
}
public saveGridConfig(id) {
}
/**
/**
* Should overridden by sub-classes , load the recent process max limit.
*/
loadRecentProcessLimit() {
}
public loadRecentProcessLimit() {
}
/**
/**
* Should overridden by sub-classes , save the recent process max limit.
*/
saveRecentProcessLimit(num) {
}
public saveRecentProcessLimit(num) {
}
/**
/**
* Should overridden by sub-classes , load the layout information of grid view.
*/
loadGridLayoutInfo() {
public loadGridLayoutInfo() {
}
}
/**
/**
* Should overridden by sub-classes , save the layout information of grid view.
*/
saveGridLayoutInfo(layoutInfo) {
public saveGridLayoutInfo(layoutInfo) {
}
}
/**
/**
* Should overridden by sub-classes , remove layout information of grid view.
*/
removeGridLayoutInfo() {
}
public removeGridLayoutInfo() {
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import GridLayoutConfigs from '../configs/GridLayoutConfigs.ets'
import LayoutConfigManager from '../../manager/LayoutConfigManager.ets'
import FileUtils from '../../utils/FileUtils.ets'
import ILayoutConfig from './ILayoutConfig.ets'
import FeatureAbility from '@ohos.ability.featureability';
const defaultLayoutInfoFilePath = "/data/accounts/account_0/applications/com.ohos.launcher/com.ohos.launcher/assets/launcher/resources/rawfile/layoutInfo.json";
export default class SettingsModel {
private mLayoutConfig: ILayoutConfig;
private mGridConfig = 1;
private mGridLayoutTable = GridLayoutConfigs.GridLayoutTable;
constructor() {
this.mLayoutConfig = LayoutConfigManager.getLayoutConfig();
}
/**
* Get the grid view presetting collection of layout config information table.
*
* @return {object} Grid view presetting collection object.
*/
public getGridLayoutTable(): any {
return this.mGridLayoutTable;
}
/**
* Get default layout information of grid view.
*
* @return {object} Default layout information of grid view.
*/
public getDefaultLayoutInfo(): any {
return FileUtils.readJsonFile(defaultLayoutInfoFilePath);
}
/**
* Get layout config of grid view.
*
* @return {object} Layout config of grid view.
*/
public getGridConfig(): any {
this.mGridConfig = this.mLayoutConfig.getGridConfig();
return this.mGridLayoutTable[this.mGridConfig];
}
/**
* Set layout config id of grid view.
*
* @param {string} id - Layout config id of grid view.
*/
public setGridConfig(id) {
this.mLayoutConfig.setGridConfig(id);
}
/**
* Get recent process max limit.
*
* @return {number} recent process max limit.
*/
public getRecentProcessLimit(): any {
return this.mLayoutConfig.getRecentProcessLimit();
}
/**
* Set recent process max limit.
*
* @param {number} num - Recent process max limit.
*/
public setRecentProcessLimit(num) {
this.mLayoutConfig.setRecentProcessLimit(num);
}
/**
* Get the layout view type.
*
* @return {string} Layout view type, should one of 'Grid' or 'List' which is stored in LayoutConstants class.
*/
public getAppPageStartConfig(): any {
return this.mLayoutConfig.getAppPageStartConfig();
}
/**
* Set the layout view type.
*
* @param {string} type - Layout view type, should one of 'Grid' or 'List' which is stored in LayoutConstants class.
*/
public setAppPageStartConfig(type) {
this.mLayoutConfig.setAppPageStartConfig(type);
}
/**
* Get layout information of grid view.
*
* @return {object} layout information.
*/
public getLayoutInfo(): any {
return this.mLayoutConfig.getGridLayoutInfo();
}
/**
* Set layout information of grid view.
*/
public setLayoutInfo(layoutInfo) {
this.mLayoutConfig.setGridLayoutInfo(layoutInfo);
}
/**
* Remove layout information of grid view.
*/
public deleteLayoutInfo() {
this.mLayoutConfig.deleteGridLayoutInfo();
}
/**
* Close settings.
*/
closeSettings() {
console.info("Launcher settings SettingsModel closeSettings start")
FeatureAbility.terminateAbility()
.then(data => console.info("Launcher settings SettingsModel closeSettings promise::then : " + data))
.catch(error => console.info("Launcher settings SettingsModel closeSettings promise::catch : " + error));
console.info("Launcher settings SettingsModel closeSettings end ");
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ILayoutConfig from './ILayoutConfig.ets'
import Storage from '@ohos.data.storage';
import DefaultLayoutConfig from '../configs/DefaultLayoutConfig.ets'
const APP_PAGE_START_CONFIG = 'AppStartPageType';
const GRID_CONFIG = "GridConfig";
const RECENT_PROCESS_LIMIT = "RecentProcessLimit";
const GRID_LAYOUT_INFO = "GridLayoutInfo";
const PREFERENCES_PATH = '/data/accounts/account_0/appdata/com.ohos.launcher/sharedPreference/LauncherPreference';
let mPreferences = Storage.getStorageSync(PREFERENCES_PATH);
let mGridLayoutInfo = {
layoutDescription: {
pageCount: 1,
row: 5,
column: 4
},
layoutInfo: [],
bottomBarInfo: []
}
export default class StorageLayoutConfig extends ILayoutConfig {
constructor() {
super()
}
/**
* Should overridden by sub-classes , load the launcher layout view type.
*/
public loadAppPageStartConfig() {
console.info('Launcher mPreferences get APP_PAGE_START_CONFIG');
let data = mPreferences.getSync(APP_PAGE_START_CONFIG, DefaultLayoutConfig.DefaultAppPageStartConfig);
console.info('Launcher mPreferences get' + data);
return data;
}
/**
* Should overridden by sub-classes , save the launcher layout view type.
*/
public saveAppPageStartConfig(type) {
console.info('Launcher mPreferences put type' + type);
mPreferences.putSync(APP_PAGE_START_CONFIG, type);
mPreferences.flushSync();
console.info('Launcher mPreferences put type flush');
}
/**
* Should overridden by sub-classes , load the launcher grid view layout config id.
*/
public loadGridConfig() {
console.info('Launcher mPreferences get GRID_CONFIG');
let data = mPreferences.getSync(GRID_CONFIG, DefaultLayoutConfig.DefaultGridConfig);
console.info('Launcher mPreferences get' + data);
return data;
}
/**
* Should overridden by sub-classes , save the launcher grid view layout config id.
*/
public saveGridConfig(id) {
console.info('Launcher mPreferences put id' + id);
mPreferences.putSync(GRID_CONFIG, id);
mPreferences.flushSync();
console.info('Launcher mPreferences put id flush');
}
/**
* Should overridden by sub-classes , load the recent process max limit.
*/
public loadRecentProcessLimit() {
console.info('Launcher mPreferences get');
let data = mPreferences.getSync(RECENT_PROCESS_LIMIT, DefaultLayoutConfig.DefaultRecentProcessLimit);
console.info('Launcher mPreferences get' + data);
return data;
}
/**
* Should overridden by sub-classes , save the recent process max limit.
*/
public saveRecentProcessLimit(num) {
console.info('Launcher mPreferences put num' + num);
mPreferences.putSync(RECENT_PROCESS_LIMIT, num);
mPreferences.flushSync();
console.info('Launcher mPreferences put num flush');
}
/**
* Should overridden by sub-classes , load the layout information of grid view.
*/
public loadGridLayoutInfo() {
console.info('Launcher StorageLayoutConfig loadGridLayoutInfo start');
let data: any = mPreferences.getSync(GRID_LAYOUT_INFO, '');
console.info('Launcher StorageLayoutConfig loadGridLayoutInfo ' + data);
if (data == '') {
return mGridLayoutInfo;
} else {
return JSON.parse(data);
}
}
/**
* Should overridden by sub-classes , save the layout information of grid view.
*/
public saveGridLayoutInfo(layoutInfo) {
console.info('Launcher StorageLayoutConfig saveGridLayoutInfo start');
mPreferences.putSync(GRID_LAYOUT_INFO, JSON.stringify(layoutInfo));
mPreferences.flushSync();
console.info('Launcher StorageLayoutConfig saveGridLayoutInfo end');
}
/**
* Should overridden by sub-classes , remove layout information of grid view.
*/
public removeGridLayoutInfo() {
console.info('Launcher StorageLayoutConfig removeGridLayoutInfo start');
mPreferences.deleteSync(GRID_LAYOUT_INFO);
console.info('Launcher StorageLayoutConfig removeGridLayoutInfo start');
}
}

View File

@ -12,13 +12,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import StorageLayoutConfig from '../common/model/StorageLayoutConfig.ets'
/**
* A Constant class includes layout type Strings.
*/
const LayoutConstants = {
Grid: "Grid",
List: "List"
export default class LayoutConfigManager {
public static getLayoutConfig() {
return new StorageLayoutConfig();
}
}
export default LayoutConstants;

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Resmgr from '@ohos.resourceManager';
import AppResourceCacheManager from '../cache/AppResourceCacheManager.ets'
import CheckEmptyUtils from '../utils/CheckEmptyUtils.ets'
const KEY_ICON = "icon";
const KEY_NAME = "name";
export default class ResourceManager {
private static resourceManager: ResourceManager = new ResourceManager();
private mAppResourceCacheManager: AppResourceCacheManager;
private constructor() {
this.mAppResourceCacheManager = new AppResourceCacheManager();
}
public static getInstance(): ResourceManager{
return this.resourceManager;
}
getAppIcon(path, bundleName, callback, defaultAppIcon) {
if (path == null || path == undefined || path == "" || path <= 0) {
console.info('Launcher ResourceManager getAppIcon iconId > ' + defaultAppIcon);
callback(defaultAppIcon);
} else {
let iconBase64 = this.mAppResourceCacheManager.getCache(bundleName, KEY_ICON);
if (iconBase64 == undefined || iconBase64 == null || iconBase64 == '') {
Resmgr.getResourceManager(bundleName)
.then(item => {
if (CheckEmptyUtils.isEmpty(item)) {
console.error("Launcher ResourceManager getAppIcon getResourceManager ERROR! item is empty");
}
console.info('Launcher ResourceManager getAppIcon data>' + item);
item.getMediaBase64(path, (error, value) => {
console.info('Launcher ResourceManager getAppIcon getMediaBase64 value>' + value);
if (value != null) {
this.mAppResourceCacheManager.setCache(bundleName, KEY_ICON, value);
callback(value);
}
else {
callback(defaultAppIcon);
}
});
})
.catch(e => {
console.error("Launcher ResourceManager getAppIcon error ");
callback(defaultAppIcon);
});
} else {
callback(iconBase64);
}
}
}
getAppName(labelId, bundleName, appName, callback) {
if (labelId == null || labelId == undefined || labelId == "" || labelId <= 0) {
console.info('Launcher ResourceManager getAppName callback ' + appName);
this.mAppResourceCacheManager.setCache(bundleName, KEY_NAME, appName);
callback(appName);
} else {
let name = this.mAppResourceCacheManager.getCache(bundleName, KEY_NAME);
if (name == undefined || name == null || name == '') {
Resmgr.getResourceManager(bundleName)
.then(item => {
console.info('Launcher ResourceManager getAppName getResourceManager labelId' + labelId);
item.getString(labelId, (error, value) => {
if (CheckEmptyUtils.checkStrIsEmpty(value)) {
console.error("Launcher AppModel getAppName getString ERROR! value is empty");
}
console.info('Launcher ResourceManager getAppName getString value>' + value);
if (value != null) {
this.mAppResourceCacheManager.setCache(bundleName, KEY_NAME, value);
callback(value);
} else {
callback(appName);
}
});
})
.catch(e => {
console.error("Launcher ResourceManager getAppName error ")
callback(appName);
});
} else {
callback(name);
}
}
}
/**
* Get app resource cache.
*
* @param {string} bundleName - bundleName of target file
* @param {string} key - key of the cache
*/
getAppResourceCache(bundleName, key) {
return this.mAppResourceCacheManager.getCache(bundleName, key);
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ResourceManager from '../manager/ResourceManager.ets'
import DefaultConstants from '../common/constants/DefaultConstants.ets'
@Component
export default
struct AppIcon {
@Prop iconSize: number;
@Prop @Watch("updateIcon") appIcon: string;
@Prop bundleName: string;
@Prop labelId: string;
@State icon: string = ' ';
private mResourceManager;
private mDefaultAppIcon;
public aboutToAppear(): void {
this.mDefaultAppIcon = DefaultConstants.DEFAULT_ICON;
this.icon = this.mDefaultAppIcon;
this.mResourceManager = ResourceManager.getInstance();
this.mResourceManager.getAppIcon(this.appIcon, this.bundleName, this.iconLoadCallback.bind(this), this.mDefaultAppIcon);
}
public iconLoadCallback(image) {
this.icon = image;
}
public updateIcon() {
this.mResourceManager.getAppIcon(this.appIcon, this.bundleName, this.iconLoadCallback.bind(this), this.mDefaultAppIcon);
}
build() {
Column() {
Image(this.icon)
}
.width(this.iconSize)
.height(this.iconSize)
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ResourceManager from '../manager/ResourceManager.ets'
import DefaultConstants from '../common/constants/DefaultConstants.ets'
@Component
export default
struct GridName {
@State nameSize: number = 20;
@Prop nameHeight: number;
@Prop nameWidth: number;
@Prop @Watch("updateName") appName: string;
@State name: string = ' '
@Prop bundleName: string;
@Prop labelId: string;
private mResourceManager;
public aboutToAppear(): void {
this.mResourceManager = ResourceManager.getInstance();
this.mResourceManager.getAppName(this.labelId, this.bundleName, this.appName, this.appNameLoadCallback.bind(this));
}
public appNameLoadCallback(name: string) {
this.name = name;
}
public updateName() {
this.mResourceManager.getAppName(this.labelId, this.bundleName, this.appName, this.appNameLoadCallback.bind(this));
}
build() {
Column() {
Text(this.name)
.fontSize(this.nameSize)
.fontColor(DefaultConstants.DEFAULT_FONT_COLOR)
}
.width(this.nameWidth)
.height(this.nameHeight)
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ResourceManager from '../manager/ResourceManager.ets'
import DefaultConstants from '../common/constants/DefaultConstants.ets'
@Component
export default
struct ListName {
@State nameSize: number = 20;
@Prop @Watch("updateName") appName: string;
@State name: string = ''
@Prop bundleName: string;
@Prop labelId: string;
private mResourceManager;
public aboutToAppear(): void {
this.mResourceManager = ResourceManager.getInstance();
this.mResourceManager.getAppName(this.labelId, this.bundleName, this.appName, this.appNameLoadCallback.bind(this));
}
public appNameLoadCallback(name: string) {
this.name = name;
}
public updateName() {
this.mResourceManager.getAppName(this.labelId, this.bundleName, this.appName, this.appNameLoadCallback.bind(this));
}
build() {
Column() {
Text(this.name)
.fontSize(this.nameSize)
.fontColor(DefaultConstants.DEFAULT_FONT_COLOR)
.margin({ left: 20 })
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -13,42 +13,43 @@
* limitations under the License.
*/
import Pinyin from './Pinyin.js'
import Pinyin from './Pinyin.ets'
/**
* An util that provides sort for pinyin and other character(such as $#%^).
*/
export default class PinyinSort {
/**
private pinyin;
/**
* Constructor.
*
* @param {boolean} checkPolyphone - Whether to check for polyphonic words.
* @param {number} charCase - Output pinyin case mode, 0- first letter capitalization; 1- All lowercase; 2 - all uppercase.
*/
constructor(checkPolyphone = false, charCase = 1) {
this.pinyin = new Pinyin({
checkPolyphone: checkPolyphone,
charCase: charCase
});
}
constructor(checkPolyphone = false, charCase = 1) {
this.pinyin = new Pinyin({
checkPolyphone: checkPolyphone,
charCase: charCase
});
}
/**
/**
* Sort data for appinfo,compared by appName.
*
* @param {object} a - appinfo for compare.
* @return {object} b - appinfo for compare.
*/
sortByAppName(a, b) {
return this.#getChar(a.AppName) - this.#getChar(b.AppName);
}
sortByAppName(a, b) {
return this.getChar(a.AppName) - this.getChar(b.AppName);
}
/**
/**
* Get first char for pinyin.
*
* @param {string} str - chinese string.
* @return {char} charCode.
*/
#getChar = (str) => {
return this.pinyin.getFullChars(str).charCodeAt(0);
}
private getChar(str) {
return this.pinyin.getFullChars(str).charCodeAt(0);
}
}

View File

@ -0,0 +1,12 @@
{
"string": [
{
"name": "common_MainAbility",
"value": "common_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
}
]
}

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,19 @@
apply plugin: 'com.huawei.ohos.library'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 6
defaultConfig {
compatibleSdkVersion 4
}
supportSystem "standard"
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
}
dependencies {
}

View File

@ -0,0 +1,22 @@
{
"app": {
"bundleName": "com.ohos.launcher",
"vendor": "ohos",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"deviceConfig": {},
"module": {
"package": "com.ohos.launcher",
"deviceType": [
"phone"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "layoutmanager",
"moduleType": "har"
}
}
}

View File

@ -12,12 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const DefaultLayoutConfig = {
DefaultAppPageStartConfig: 'Grid',
DefaultGridConfig: 1,
DefaultRecentProcessLimit: 20,
DefaultRecentProcessLimitArray: [5,10,15,20],
DefaultAppPageStartConfig: 'Grid',
DefaultGridConfig: 1,
DefaultRecentProcessLimit: 20,
DefaultRecentProcessLimitArray: [5, 10, 15, 20],
}
export default DefaultLayoutConfig;

View File

@ -12,19 +12,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import SettingsModel from './common/model/SettingsModel.js'
export default {
data: {
settingsModel: new SettingsModel()
const GridLayoutConfigs = {
GridLayoutTable: [
{
id: 0,
layout: '4X4',
row: 4,
column: 4
},
onCreate() {
console.info('Launcher settings AceApplication onCreate');
{
id: 1,
layout: '5X4',
row: 5,
column: 4
},
onDestroy() {
console.info('Launcher settings AceApplication onDestroy');
}
};
{
id: 2,
layout: '6X4',
row: 6,
column: 4
},
]
}
export default GridLayoutConfigs;

View File

@ -12,34 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../css/CommonPageStyle.css';
.systemApp {
height: 100%;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.bottomBarContainer {
display: flex;
justify-content: center;
flex-direction: row;
}
.app-box {
display: flex;
flex-direction: column;
border-radius: 20px;
justify-content: center;
}
.app-box:focus {
transform: scale(1.1, 1.1);
}
export default class StyleConstants {
public static DEFAULT_FONT_COLOR: string = '#ffffff'
public static DEFAULT_ICON: string = '/common/pics/icon.png'
public static DEFAULT_BACKGROUND_IMAGE: string = '/common/pics/img_wallpaper_default.jpg'
public static DEFAULT_APP_NAME_SIZE: number= 20;
public static DEFAULT_APP_ITEM_WIDTH: number = 70;
public static DEFAULT_APP_ITEM_HEIGHT: number = 95;
public static PERCENTAGE_100: string = '100%'
public static PERCENTAGE_85: string = '85%'
public static PERCENTAGE_25: string = '25%'
public static PERCENTAGE_15: string = '15%'
}

View File

@ -0,0 +1,920 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import BaseAppPresenter from '../../../../../../../../common/src/main/ets/default/base/BaseAppPresenter.ets';
import Prompt from '@ohos.prompt';
const PROHIBITED = '禁止卸载'
const UNINSTALL_SUCCEEDED = '卸载成功'
const UNINSTALL_DAILED = '卸载失败'
const KEY_APP_LIST = "appListInfo";
const BOTTOM_BAR_FLAG = -1;
const UNINSTALL_SUCCESS = "UNINSTALL_SUCCESS";
const UNINSTALL_FAILED = "UNINSTALL_FAILED";
const UNINSTALL_PROHIBITED = "UNINSTALL_PROHIBITED";
const PROPORTION = 0.85;
const KEY_ICON = "icon";
const KEY_NAME = "name";
const BOTTOM_BAR = 1;
const BOTTOM_BAR_ROW = -1;
export default class AppGridPresenter extends BaseAppPresenter {
private static appGridPresenter: AppGridPresenter = new AppGridPresenter();
private mBundleInfoList;
private mGridConfig;
private mScreenBottomBarTop = 0;
private pageIndex = 0;
private mGridAppsInfos;
private mPageCoordinateData = {
gridXAxis: [],
gridYAxis: [],
bottomXAxis: [],
bottomYAxis: [],
bottomSpacing: 0
};
private constructor() {
super()
this.mGridConfig = this.mSettingsModel.getGridConfig();
AppStorage.SetOrCreate('pageIndex', this.pageIndex);
}
public static getInstance(): AppGridPresenter{
return this.appGridPresenter;
}
public getGridList() {
this.mAppModel.getAppList(this.getGridListCallback.bind(this));
}
public getGridListCallback(list) {
this.mBundleInfoList = list;
this.mAppListInfoCacheManager.setCache(KEY_APP_LIST, list);
this.pagingFiltering();
}
getGridConfig() {
return this.mSettingsModel.getGridConfig();
}
private pagingFiltering() {
let appInfo = {
appGridInfo: [],
appBottomBarInfo: []
};
let appBottomBarInfo = [];
let appListInfo = [];
let info = this.getLayoutInfo();
let layoutInfo = info.layoutInfo;
let bottomInfo = info.bottomBarInfo;
for (let i = 0;i < layoutInfo.length; i++) {
for (let j = 0; j < this.mBundleInfoList.length; j++) {
if (layoutInfo[i].bundleName == this.mBundleInfoList[j].bundleName) {
appListInfo.push(
{
System: this.mBundleInfoList[j].System,
AppName: this.mBundleInfoList[j].AppName,
AppId: this.mBundleInfoList[j].AppId,
AppIcon: this.mBundleInfoList[j].AppIcon,
bundleName: this.mBundleInfoList[j].bundleName,
labelId: this.mBundleInfoList[j].labelId,
abilityName: this.mBundleInfoList[j].abilityName,
type: 0,
area: layoutInfo[i].area,
page: layoutInfo[i].page,
row: layoutInfo[i].row,
column: layoutInfo[i].column,
x: 0,
}
);
}
}
}
for (let i = 0;i < bottomInfo.length; i++) {
for (let j = 0; j < this.mBundleInfoList.length; j++) {
if (bottomInfo[i].bundleName == this.mBundleInfoList[j].bundleName) {
appBottomBarInfo.push(
{
System: this.mBundleInfoList[j].System,
AppName: this.mBundleInfoList[j].AppName,
AppId: this.mBundleInfoList[j].AppId,
AppIcon: this.mBundleInfoList[j].AppIcon,
bundleName: this.mBundleInfoList[j].bundleName,
labelId: this.mBundleInfoList[j].labelId,
abilityName: this.mBundleInfoList[j].abilityName,
type: 0,
area: bottomInfo[i].area,
page: bottomInfo[i].page,
row: bottomInfo[i].row,
column: bottomInfo[i].column,
x: 0,
}
);
}
}
}
appInfo.appGridInfo = this.integrateSwiper(appListInfo);
appInfo.appBottomBarInfo = this.integrateBottomBar(appBottomBarInfo);
AppStorage.SetOrCreate('appListInfo', appInfo);
}
private integrateSwiper(list) {
let gridAppsInfos = [];
let allPageCount = this.mSettingsModel.getLayoutInfo().layoutDescription.pageCount;
let max = allPageCount;
for (let i = 0;i < list.length; i++) {
if (max < list[i].page) {
max = list[i].page;
}
}
for (let i = 0;i < max; i++) {
gridAppsInfos.push([]);
}
for (let i = 0;i < list.length; i++) {
gridAppsInfos[list[i].page].push(list[i]);
}
this.mGridAppsInfos = gridAppsInfos;
return gridAppsInfos;
}
private integrateBottomBar(list) {
if (list.length == 5) {
AppStorage.SetOrCreate('BottomBarItemWidth', '19%');
} else if (list.length == 3) {
AppStorage.SetOrCreate('BottomBarItemWidth', '28%');
} else {
AppStorage.SetOrCreate('BottomBarItemWidth', '25%');
}
for (let i = 0;i < list.length; i++) {
list[i].x = this.mPageCoordinateData.bottomXAxis[list.length - 1][i];
}
return list;
}
private getLayoutInfo() {
let info = {
layoutInfo: [],
bottomBarInfo: []
};
info = this.mSettingsModel.getLayoutInfo();
let bottomBarInfo = info.bottomBarInfo;
let isBottomBarLegal = this.ifBottomBarRationality(bottomBarInfo);
let isLegal = this.ifLayoutRationality(info);
if (isLegal && isBottomBarLegal) {
info = this.updateLayoutInfo(info);
} else if (!isBottomBarLegal) {
let defaultInfo = this.mSettingsModel.getDefaultLayoutInfo();
let defaultBottomBarInfo = defaultInfo.bottomBarInfo;
let isDefaultLayoutLegal = this.ifLayoutRationality(defaultInfo);
let isDefaultBottomBarLegal = this.ifBottomBarRationality(defaultBottomBarInfo);
if (isDefaultLayoutLegal && isDefaultBottomBarLegal) {
info = this.updateLayoutInfo(this.mSettingsModel.getDefaultLayoutInfo());
} else {
info = this.updateLayoutInfo(this.createNewInfo());
}
} else if (isBottomBarLegal && !isLegal) {
info = this.updateLayoutInfo(this.createNewLayoutInfo());
}
this.mSettingsModel.setLayoutInfo(info);
return info;
}
private ifBottomBarRationality(bottomBarInfo) {
if (bottomBarInfo == undefined || this.ifBottomBarIsBigger(bottomBarInfo)) {
return false;
}
return true;
}
private ifBottomBarIsBigger(bottomBarInfo) {
if (bottomBarInfo.length > 5) {
return true;
}
return false;
}
ifLayoutRationality = (info) => {
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
//verify whether the info is null.
if (this.ifInfoIsNull(info)) {
return false;
}
let layoutDescription = info.layoutDescription;
//verify whether the layoutDescription is different.
if (this.ifDescriptionIsDiffrent(layoutDescription, row, column)) {
return false;
}
let layoutInfo = info.layoutInfo;
//verify whether the layoutInfo's row and column is more than standard.
if (this.ifColumnOrRowAreBigger(layoutInfo, row, column)) {
return false;
}
//verify whether the layoutInfo's position is duplicated.
if (this.ifDuplicatePosition(layoutInfo)) {
return false;
}
//verify whether the layoutInfo's bundleName is duplicated.
if (this.ifDuplicateBundleName(layoutInfo)) {
return false;
}
return true;
}
private ifInfoIsNull(info) {
if (info == undefined || info == '' || info == {} || info == null) {
return true;
}
return false;
}
private ifDescriptionIsDiffrent(layoutDescription, row, column) {
if (row != layoutDescription.row || column != layoutDescription.column) {
return true;
}
return false;
}
private ifColumnOrRowAreBigger(layoutInfo, row, column) {
for (let i = 0; i < layoutInfo.length; i++) {
//column or row are bigger than legal num
if (layoutInfo[i].column >= column || layoutInfo[i].row >= row) {
return true;
}
}
return false;
}
ifDuplicatePosition(layoutInfo) {
for (let i = 0; i < layoutInfo.length; i++) {
for (let j = layoutInfo.length - 1; j > 0 && j > i; j--) {
if (layoutInfo[i].page == layoutInfo[j].page && layoutInfo[i].row == layoutInfo[j].row && layoutInfo[i].column == layoutInfo[j].column) {
return true;
}
}
}
return false;
}
ifDuplicateBundleName(layoutInfo) {
let count = {};
for (let i = 0; i < layoutInfo.length; i++) {
if (count[layoutInfo[i].bundleName] == undefined || count[layoutInfo[i].bundleName] == null || count[layoutInfo[i].bundleName] == '') {
count[layoutInfo[i].bundleName] = 0;
} else if (++ count[layoutInfo[i].bundleName] > 1) {
return true;
}
}
return false;
}
private updateLayoutInfo(info) {
let layoutDescription = info.layoutDescription;
let layoutInfo = info.layoutInfo;
let bottomBarInfo = info.bottomBarInfo;
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
let newApp = [];
layoutDescription.row = row;
layoutDescription.column = column;
//Detect newly installed apps
for (let i in this.mBundleInfoList) {
let sign = false;
for (let j in layoutInfo) {
if (this.mBundleInfoList[i].bundleName == layoutInfo[j].bundleName) {
sign = true;
break;
}
}
if (!sign) {
newApp.push(this.mBundleInfoList[i]);
}
}
for (let i = newApp.length - 1; i >= 0; i--) {
let sign = false;
for (let j = 0; j < bottomBarInfo.length; j++) {
if (newApp[i].bundleName == bottomBarInfo[j].bundleName) {
sign = true;
break;
}
}
if (sign) {
newApp.splice(i, 1);
}
}
//Detect uninstalled apps
for (let i in layoutInfo) {
let sign = false;
for (let j in this.mBundleInfoList) {
if (layoutInfo[i].bundleName == this.mBundleInfoList[j].bundleName) {
sign = true;
break;
}
}
if (!sign) {
layoutInfo.splice(i, 1);
}
}
for (let i in bottomBarInfo) {
let sign = false;
for (let j in this.mBundleInfoList) {
if (bottomBarInfo[i].bundleName == this.mBundleInfoList[j].bundleName) {
sign = true;
break;
}
}
if (!sign) {
bottomBarInfo.splice(i, 1);
}
}
//The latest info position in the page
let existNumber = this.getExistNumber(layoutInfo);
//Add new app
for (let i = 0; i < newApp.length; i++) {
layoutInfo.push({
bundleName: newApp[i].bundleName,
type: 0,
area: [1, 1],
page: Math.floor((i + existNumber) / (column * row)),
row: Math.floor((i + existNumber) % (column * row) / column),
column: Math.floor((i + existNumber) % (column * row) % column),
});
}
// layoutDescription.pageCount = Math.ceil((newApp.length + existNumber) / (column * row));
let pageCount = Math.ceil((newApp.length + existNumber) / (column * row));
if (pageCount < this.mSettingsModel.getLayoutInfo().layoutDescription.pageCount) {
layoutDescription.pageCount = this.mSettingsModel.getLayoutInfo().layoutDescription.pageCount;
} else {
layoutDescription.pageCount = pageCount;
}
info.layoutDescription = layoutDescription;
info.layoutInfo = layoutInfo;
info.bottomBarInfo = bottomBarInfo;
return info;
}
private getExistNumber(layoutInfo) {
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
let existNumber = 0;
for (let i = 0; i < layoutInfo.length; i++) {
let Page = layoutInfo[i].page;
let Row = layoutInfo[i].row;
let Column = layoutInfo[i].column;
let result = (Page * column * row) + (Row * column) + (Column + 1);
if (result > existNumber) {
existNumber = result;
}
}
return existNumber;
}
private createNewInfo() {
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
let layoutNum = this.mBundleInfoList.length;
let maxPerPage = column * row;
let pageNum = Math.ceil(layoutNum / maxPerPage);
let newLayoutInfo = {
layoutDescription: {},
layoutInfo: [],
bottomBarInfo: []
};
newLayoutInfo.layoutDescription = {
"pageCount": pageNum,
"row": row,
"column": column,
}
newLayoutInfo.layoutInfo = [];
newLayoutInfo.bottomBarInfo = [];
return newLayoutInfo;
}
private createNewLayoutInfo() {
let info = this.mSettingsModel.getLayoutInfo();
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
let layoutNum = info.layoutInfo.length;
let maxPerPage = column * row;
let pageNum = Math.ceil(layoutNum / maxPerPage);
let newLayoutInfo = {
layoutDescription: {},
layoutInfo: [],
bottomBarInfo: []
};
newLayoutInfo.layoutDescription = {
"pageCount": pageNum,
"row": row,
"column": column
}
newLayoutInfo.layoutInfo = [];
if (info.bottomBarInfo == undefined) {
newLayoutInfo.bottomBarInfo = [];
} else {
newLayoutInfo.bottomBarInfo = info.bottomBarInfo;
}
return newLayoutInfo;
}
public regroupDataAfterInstall(callbackList) {
this.mBundleInfoList = callbackList;
this.mAppListInfoCacheManager.setCache(KEY_APP_LIST, callbackList);
this.pagingFiltering();
}
public regroupDataAfterUnInstall(result) {
console.info("Launcher AppGridView getUninstallApp callback = " + result);
if (result === UNINSTALL_PROHIBITED) {
Prompt.showToast({
message: PROHIBITED
});
} else if (result === UNINSTALL_SUCCESS) {
Prompt.showToast({
message: UNINSTALL_SUCCEEDED
});
} else if (result === UNINSTALL_FAILED) {
Prompt.showToast({
message: UNINSTALL_DAILED
});
}
}
public openApplication(abilityName, bundleName) {
this.jumpTo(abilityName, bundleName);
}
public intoSetting() {
console.info("Launcher AppGridView intoSetting");
this.jumpToSetting();
}
/**
* Longpress event for launcher.
*/
public onPageLongPress() {
AppStorage.SetOrCreate('blankPageBtnText', this.getBlankPageBtnStr());
}
/**
* Get strings for addBlankPageButton.
*
* @return {string} AddBlankPageButton Strings.
*/
public getBlankPageBtnStr() {
return this.isBlankPage() ? $r('app.string.deleteBlankPage') : $r('app.string.addBlankPage');
}
public isBlankPage() {
if (this.mGridAppsInfos[this.pageIndex].length == undefined ) {
return true;
}
if(this.mGridAppsInfos[this.pageIndex].length === 0) {
return true;
}
return false;
}
public changeIndex(idx) {
this.pageIndex = idx;
AppStorage.SetOrCreate('pageIndex', this.pageIndex);
}
public getIndex() {
return this.pageIndex;
}
/**
* Add or delete the choosen blank page.
*/
public addOrDeleteBlankPage() {
if (this.isBlankPage()) {
this.deleteBlankPage();
} else {
this.addBlankPage();
}
}
/**
* Add a blank page.
*/
public addBlankPage() {
console.info("Launcher AppGridView addBlankPage");
let allPageCount = this.getGridPageCount() + 1;
this.setGridPageCount(allPageCount);
this.pagingFiltering();
this.pageIndex = allPageCount - 1;
AppStorage.SetOrCreate('pageIndex', this.pageIndex);
}
/**
* Get pageCount.
*
* @return {number} PageCount.
*/
public getGridPageCount() {
return this.mSettingsModel.getLayoutInfo().layoutDescription.pageCount;
}
/**
* Set pageCount.
*
* @param {number} pageCount - PageCount.
*/
private setGridPageCount(pageCount) {
let gridLayoutInfo = this.mSettingsModel.getLayoutInfo();
gridLayoutInfo.layoutDescription.pageCount = pageCount;
this.mSettingsModel.setLayoutInfo(gridLayoutInfo);
}
/**
* Delete the choosen blank page.
*/
private deleteBlankPage() {
console.info("Launcher AppGridView deleteBlankPage");
this.deleteGridPage(this.pageIndex);
this.pageIndex = this.pageIndex - 1;
AppStorage.SetOrCreate('pageIndex', this.pageIndex);
this.setGridPageCount(this.mSettingsModel.getLayoutInfo().layoutDescription.pageCount - 1);
this.pagingFiltering();
}
/**
* Delete blank page.
*
* @param {number} pageIndex - Index of the page which is to be deleted.
*/
private deleteGridPage(pageIndex) {
let info = this.mSettingsModel.getLayoutInfo();
let layoutInfo = info.layoutInfo;
for (let element of layoutInfo) {
if (element.page > pageIndex) {
element.page = element.page - 1;
}
}
info.layoutInfo = layoutInfo;
this.mSettingsModel.setLayoutInfo(info);
}
public layoutAdjustment(startInfo, endInfo, bottomBar) {
if (startInfo.row == BOTTOM_BAR_FLAG && endInfo.row != BOTTOM_BAR_FLAG) {
this.bottomToTopAdjustment(startInfo, endInfo, bottomBar);
} else if (startInfo.row == BOTTOM_BAR_FLAG && endInfo.row == BOTTOM_BAR_FLAG) {
this.bottomToBottomAdjustment(startInfo, endInfo, bottomBar);
} else if (startInfo.row != BOTTOM_BAR_FLAG && endInfo.row == BOTTOM_BAR_FLAG) {
this.topToBottomAdjustment(startInfo, endInfo, bottomBar);
} else {
this.topToTopAdjustment(startInfo, endInfo);
}
}
bottomToTopAdjustment(startInfo, endInfo, bottomBar) {
let info = this.mSettingsModel.getLayoutInfo();
let layoutInfo = info.layoutInfo;
let bottomBarInfo = info.bottomBarInfo;
let moveItem = {
bundleName: "",
type: 0,
page: -1,
row: -1,
column: -1
}
for (let i = bottomBar.length - 1; i >= 0; i--) {
if (startInfo.X >= bottomBar[i].x - this.mPageCoordinateData.bottomSpacing && startInfo.X < bottomBar[i].x + this.mPageCoordinateData.bottomSpacing) {
moveItem.bundleName = bottomBarInfo[i].bundleName;
moveItem.type = bottomBarInfo[i].type;
bottomBarInfo.splice(i, 1);
layoutInfo.push(moveItem);
break;
}
}
this.moveLayout(moveItem, endInfo, layoutInfo, moveItem);
for (let i = layoutInfo.length - 1; i >= 0; i--) {
if (layoutInfo[i].page == -1 && layoutInfo[i].column == -1 && layoutInfo[i].row == -1) {
layoutInfo.splice(i, 1);
break;
}
}
info.layoutInfo = layoutInfo;
info.bottomBarInfo = bottomBarInfo;
this.mSettingsModel.setLayoutInfo(info);
this.pagingFiltering();
}
bottomToBottomAdjustment(startInfo, endInfo, bottomBar) {
let info = this.mSettingsModel.getLayoutInfo();
let bottomBarInfo = info.bottomBarInfo;
let moveItem = {
bundleName: "",
type: 0,
page: 0,
row: 0,
column: 0,
}
for (let i = bottomBar.length - 1; i >= 0; i--) {
if (startInfo.X >= bottomBar[i].x - this.mPageCoordinateData.bottomSpacing && startInfo.X < bottomBar[i].x + this.mPageCoordinateData.bottomSpacing) {
moveItem.bundleName = bottomBarInfo[i].bundleName;
moveItem.type = bottomBarInfo[i].type;
moveItem.page = bottomBarInfo[i].page;
moveItem.row = bottomBarInfo[i].row;
moveItem.column = bottomBarInfo[i].column;
bottomBarInfo.splice(i, 1);
}
}
for (let i = 0; i < bottomBar.length; i++) {
if (i == 0 && endInfo.X < bottomBar[i].x) {
bottomBarInfo.splice(i, 0, moveItem);
break;
} else if (i != bottomBar.length - 1 && endInfo.X >= bottomBar[i].x && endInfo.X < bottomBar[i + 1].x) {
bottomBarInfo.splice(i, 0, moveItem);
break;
} else if (i == bottomBar.length - 1 && endInfo.X >= bottomBar[i].x) {
bottomBarInfo.push(moveItem);
break;
}
}
info.bottomBarInfo = bottomBarInfo;
this.mSettingsModel.setLayoutInfo(info);
this.pagingFiltering();
}
topToBottomAdjustment(startInfo, endInfo, bottomBar) {
let info = this.mSettingsModel.getLayoutInfo();
let layoutInfo = info.layoutInfo;
let bottomBarInfo = info.bottomBarInfo;
let moveItem = {
bundleName: "",
type: 0,
page: 0,
row: 0,
column: 0
};
for (let i = layoutInfo.length - 1; i >= 0; i--) {
if (layoutInfo[i].page == startInfo.page && layoutInfo[i].row == startInfo.row && layoutInfo[i].column == startInfo.column) {
moveItem.bundleName = layoutInfo[i].bundleName;
moveItem.type = layoutInfo[i].type;
moveItem.page = layoutInfo[i].page;
moveItem.row = layoutInfo[i].row;
moveItem.column = layoutInfo[i].column;
layoutInfo.splice(i, 1);
}
}
if (bottomBar.length == 0) {
bottomBarInfo.push(moveItem);
} else if (bottomBar.length > 0 && bottomBar.length < 5) {
for (let i = 0; i < bottomBar.length; i++) {
if (i != bottomBar.length - 1 && bottomBar[i].x < endInfo.X && bottomBar[i + 1].x > endInfo.X) {
bottomBarInfo.splice(i + 1, 0, moveItem);
break;
} else if (i == 0 && endInfo.X < bottomBar[i].x) {
bottomBarInfo.splice(i, 0, moveItem);
break;
} else if (i == bottomBar.length - 1 && endInfo.X > bottomBar[i].x) {
bottomBarInfo.push(moveItem);
break;
}
}
} else if (bottomBar.length >= 5) {
this.swapAppIcon(moveItem, endInfo, bottomBar, bottomBarInfo);
layoutInfo.push(moveItem);
}
info.layoutInfo = layoutInfo;
info.bottomBarInfo = bottomBarInfo;
this.mSettingsModel.setLayoutInfo(info);
this.pagingFiltering();
}
topToTopAdjustment(startInfo, endInfo) {
let info = this.mSettingsModel.getLayoutInfo();
let layoutInfo = info.layoutInfo;
this.moveLayout(startInfo, endInfo, layoutInfo, startInfo);
info.layoutInfo = layoutInfo;
this.mSettingsModel.setLayoutInfo(info);
this.pagingFiltering();
}
private swapAppIcon = (moveItem, endInfo, bottomBar, bottomBarInfo) => {
let tempInfo = {
bundleName: moveItem.bundleName,
type: moveItem.type,
page: moveItem.page,
row: moveItem.row,
column: moveItem.column
}
for (let i = 0; i < bottomBar.length; i++) {
if (endInfo.X < bottomBar[i].x && i == 0) {
moveItem.bundleName = bottomBarInfo[i].bundleName;
moveItem.type = bottomBarInfo[i].type;
bottomBarInfo[i].bundleName = tempInfo.bundleName;
bottomBarInfo[i].type = tempInfo.type;
bottomBarInfo[i].page = tempInfo.page;
bottomBarInfo[i].row = tempInfo.row;
bottomBarInfo[i].column = tempInfo.column;
break;
} else if (i != bottomBar.length - 1 && endInfo.X >= bottomBar[i].x && endInfo.X < bottomBar[i + 1].x) {
moveItem.bundleName = bottomBarInfo[i].bundleName;
moveItem.type = bottomBarInfo[i].type;
bottomBarInfo[i].bundleName = tempInfo.bundleName;
bottomBarInfo[i].type = tempInfo.type;
bottomBarInfo[i].page = tempInfo.page;
bottomBarInfo[i].row = tempInfo.row;
bottomBarInfo[i].column = tempInfo.column;
break;
} else if (i == bottomBar.length - 1 && endInfo.X >= bottomBar[i].x) {
moveItem.bundleName = bottomBarInfo[i].bundleName;
moveItem.type = bottomBarInfo[i].type;
bottomBarInfo[i].bundleName = tempInfo.bundleName;
bottomBarInfo[i].type = tempInfo.type;
bottomBarInfo[i].page = tempInfo.page;
bottomBarInfo[i].row = tempInfo.row;
bottomBarInfo[i].column = tempInfo.column;
break;
}
}
}
private moveLayout = (source, destination, layoutInfo, startInfo) => {
let couldMoveForward = this.moveLayoutForward(source, destination, layoutInfo, startInfo);
if (couldMoveForward) return;
this.moveLayoutBackward(source, destination, layoutInfo, startInfo);
}
/**
* Icons go forwards.
*
* @param {object} source - Start position information.
* @param {object} destination - End position information.
* @param {object} layoutInfo - Current layout information.
* @param {object} startInfo - Recursion start position information.
* @return {boolean} Move result.
*/
private moveLayoutForward = (source, destination, layoutInfo, startInfo) => {
let startLayoutInfo = layoutInfo.find(item => {
return item.page == source.page && item.row == source.row && item.column == source.column;
});
let endLayoutInfo = layoutInfo.find(item => {
return item.page == destination.page && item.row == destination.row && item.column == destination.column;
});
if (endLayoutInfo != undefined && !(endLayoutInfo.page == startInfo.page && endLayoutInfo.row == startInfo.row && endLayoutInfo.column == startInfo.column)) {
if (endLayoutInfo.row == this.mGridConfig.row - 1 && endLayoutInfo.column == this.mGridConfig.column - 1) {
return false;
}
let nextPosition = {
page: destination.page,
row: destination.column == this.mGridConfig.column - 1 ? destination.row + 1 : destination.row,
column: destination.column == this.mGridConfig.column - 1 ? 0 : destination.column + 1
}
let couldMoveForward = this.moveLayoutForward(destination, nextPosition, layoutInfo, startInfo);
if (!couldMoveForward) return false;
}
startLayoutInfo.page = destination.page;
startLayoutInfo.row = destination.row;
startLayoutInfo.column = destination.column;
return true;
}
/**
* Icons go backwards.
*
* @param {object} source - Start position information.
* @param {object} destination - End position information.
* @param {object} layoutInfo - Current layout information.
* @param {object} startInfo - Recursion start position information.
* @return {boolean} Move result.
*/
private moveLayoutBackward = (source, destination, layoutInfo, startInfo) => {
let startLayoutInfo = layoutInfo.find(item => {
return item.page == source.page && item.row == source.row && item.column == source.column;
});
let endLayoutInfo = layoutInfo.find(item => {
return item.page == destination.page && item.row == destination.row && item.column == destination.column;
});
if (endLayoutInfo != undefined && !(endLayoutInfo.page == startInfo.page && endLayoutInfo.row == startInfo.row && endLayoutInfo.column == startInfo.column)) {
if (endLayoutInfo.row == 0 && endLayoutInfo.column == 0) {
return false;
}
let nextPosition = {
page: destination.page,
row: (destination.column == 0 && destination.row > 0) ? destination.row - 1 : destination.row,
column: destination.column == 0 ? this.mGridConfig.column - 1 : destination.column - 1
}
let couldMoveBackward = this.moveLayoutBackward(destination, nextPosition, layoutInfo, startInfo);
if (!couldMoveBackward) return false;
}
startLayoutInfo.page = destination.page;
startLayoutInfo.row = destination.row;
startLayoutInfo.column = destination.column;
return true;
}
public calculateCoordinate(): void{
let column = this.mGridConfig.column;
let row = this.mGridConfig.row;
this.mPageCoordinateData.gridXAxis = [];
this.mPageCoordinateData.gridYAxis = [];
this.mPageCoordinateData.bottomXAxis = [];
this.mPageCoordinateData.bottomYAxis = [];
for (let i = 1; i <= row; i++) {
let touchPositioningY = (this.mScreenHeight * PROPORTION / row) * i;
this.mPageCoordinateData.gridYAxis.push(touchPositioningY);
}
for (let i = 1; i <= column; i++) {
let touchPositioningX = (this.mScreenWidth / column) * i;
this.mPageCoordinateData.gridXAxis.push(touchPositioningX);
}
for (let i = 0;i < 5; i++) {
this.mPageCoordinateData.bottomXAxis.push([]);
}
for (let i = 0;i < 5; i++) {
let spacing = this.mScreenWidth / (i + 1);
if (i == 0) {
this.mPageCoordinateData.bottomXAxis[i].push(spacing * 0.5);
} else if (i == 1) {
spacing = this.mScreenWidth / 4;
this.mPageCoordinateData.bottomXAxis[i].push(spacing * 1.5);
this.mPageCoordinateData.bottomXAxis[i].push(spacing * 2.5);
} else if (i == 2) {
spacing = this.mScreenWidth * 0.08;
let cellWidth = this.mScreenWidth * 0.28;
for (let j = 0;j <= i; j++) {
this.mPageCoordinateData.bottomXAxis[i].push(spacing + (cellWidth * (j + 0.5)));
}
} else if (i == 3) {
for (let j = 0;j <= i; j++) {
this.mPageCoordinateData.bottomXAxis[i].push(spacing * (j + 0.5));
}
} else {
spacing = this.mScreenWidth * 0.025;
let cellWidth = this.mScreenWidth * 0.19;
for (let j = 0;j <= i; j++) {
this.mPageCoordinateData.bottomXAxis[i].push(spacing + (cellWidth * (j + 0.5)));
}
}
}
this.mPageCoordinateData.bottomSpacing = this.mScreenWidth / 10;
this.mScreenBottomBarTop = this.mScreenHeight * PROPORTION;
for (let i = 0; i < column; i++) {
this.mPageCoordinateData.bottomYAxis.push(this.mScreenBottomBarTop);
}
}
public getTouchPosition(x, y): any {
let position = {
page: this.pageIndex,
row: 0,
column: 0,
X: x,
Y: y,
};
for (let i: number = 0;i < this.mPageCoordinateData.gridXAxis.length; i++) {
if (x < this.mPageCoordinateData.gridXAxis[i]) {
position.column = i;
break;
} else {
position.column = this.mPageCoordinateData.gridXAxis.length - 1;
}
}
for (let i: number = 0;i < this.mPageCoordinateData.gridYAxis.length; i++) {
if (y < this.mPageCoordinateData.gridYAxis[i]) {
position.row = i;
break;
} else {
position.row = this.mPageCoordinateData.gridYAxis.length - 1;
}
}
if (y > this.mScreenBottomBarTop) {
position.row = BOTTOM_BAR_ROW;
}
return position
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import BaseAppPresenter from '../../../../../../../../common/src/main/ets/default/base/BaseAppPresenter.ets';
import PinyinSort from '../../../../../../../../common/src/main/ets/default/utils/PinyinSort.ets'
import Prompt from '@ohos.prompt';
const PROHIBITED = '禁止卸载'
const UNINSTALL_SUCCEEDED = '卸载成功'
const UNINSTALL_DAILED = '卸载失败'
const KEY_APP_LIST = "appListInfo";
const UNINSTALL_SUCCESS = "UNINSTALL_SUCCESS";
const UNINSTALL_FAILED = "UNINSTALL_FAILED";
const UNINSTALL_PROHIBITED = "UNINSTALL_PROHIBITED";
const KEY_NAME = "name";
export default class AppListPresenter extends BaseAppPresenter {
private static appListPresenter: AppListPresenter = new AppListPresenter();
private mPinyinSort: PinyinSort;
private constructor() {
super()
this.mPinyinSort = new PinyinSort();
}
public static getInstance(): AppListPresenter {
return this.appListPresenter;
}
public getAppList() {
this.mAppModel.getAppList(this.getListCallback.bind(this));
}
public getListCallback(list) {
this.mAppListInfoCacheManager.setCache(KEY_APP_LIST, list);
AppStorage.SetOrCreate('listInfo', list);
}
public regroupDataAfterInstall(callbackList) {
for (let item of callbackList) {
let appName = this.mResourceManager.getAppResourceCache(item.bundleName, KEY_NAME);
console.info("Launcher AppListPresenter regroupDataAfterInstall + appName = " + appName);
item.AppName = appName;
}
callbackList.sort(this.mPinyinSort.sortByAppName.bind(this.mPinyinSort));
this.mAppListInfoCacheManager.setCache(KEY_APP_LIST, callbackList);
AppStorage.SetOrCreate('listInfo', callbackList);
}
public regroupDataAfterUnInstall(result) {
console.info("Launcher AppListView getUninstallApp uninstallationResult:" + result);
if (result == UNINSTALL_PROHIBITED) {
Prompt.showToast({
message: PROHIBITED
})
} else if (result == UNINSTALL_SUCCESS) {
Prompt.showToast({
message: UNINSTALL_SUCCEEDED
});
} else if (result == UNINSTALL_FAILED) {
Prompt.showToast({
message: UNINSTALL_DAILED
});
}
}
public intoSetting() {
console.info("Launcher AppListView intoSetting");
this.jumpToSetting();
}
}

View File

@ -0,0 +1,448 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import AppGridPresenter from '../common/presenter/AppGridPresenter.ets'
import GridName from '../../../../../../../common/src/main/ets/default/uicomponents/GridName.ets'
import AppIcon from '../../../../../../../common/src/main/ets/default/uicomponents/AppIcon.ets'
import DefaultConstants from '../common/constants/DefaultConstants.ets'
let mAppGridPresenter: AppGridPresenter;
const DESIGN_WIDTH = 720;
const APP_INFO_REFRESH_DELAY = 500;
const CELLWIDTH = DefaultConstants.DEFAULT_APP_ITEM_WIDTH;
const PROPORTION = 0.85;
let mSHeight = 0;
let mScreenMagnification = 0;
@Component
export default
struct GridLayout {
@State AppItemHeight: number = DefaultConstants.DEFAULT_APP_ITEM_HEIGHT;
@State AppItemWidth: number = DefaultConstants.DEFAULT_APP_ITEM_WIDTH;
@State AppNameSize: number = DefaultConstants.DEFAULT_APP_NAME_SIZE;
@StorageLink('appListInfo') AppListInfo: {
appGridInfo: [[]],
appBottomBarInfo: []
} = { appGridInfo: [[]], appBottomBarInfo: [] };
@State mScreenHeight: number = 0;
@State mScreenWidth: number = 0;
@State moveAppX: number = 0;
@State moveAppY: number = 0;
@State moveAppShow: boolean = false;
@State moveAppInfo: any = {};
@State ScreenMagnification: number = 1;
dialogController: CustomDialogController = new CustomDialogController({
builder: ShowDialog(),
cancel: this.cancelDialog,
autoCancel: true
});
cancelDialog() {
console.info('Launcher Grid Cancel Dialog');
}
private aboutToAppear(): void {
mSHeight = this.mScreenHeight * PROPORTION;
mScreenMagnification = this.ScreenMagnification;
mAppGridPresenter = AppGridPresenter.getInstance();
mAppGridPresenter.setScreenHeight(this.mScreenHeight);
mAppGridPresenter.setScreenWidth(this.mScreenWidth);
mAppGridPresenter.calculateCoordinate();
mAppGridPresenter.getGridList();
mAppGridPresenter.registerAppListChangeCallback()
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Column() {
GridSwiper({
mAppGridInfo: this.AppListInfo.appGridInfo,
mAppBottomBarInfo: this.AppListInfo.appBottomBarInfo,
moveAppX: $moveAppX,
moveAppY: $moveAppY,
moveAppShow: $moveAppShow,
moveAppInfo: $moveAppInfo
})
}
.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.PERCENTAGE_85)
Column() {
BottomBar({
mAppBottomBarInfo: this.AppListInfo.appBottomBarInfo,
moveAppX: $moveAppX,
moveAppY: $moveAppY,
moveAppShow: $moveAppShow,
moveAppInfo: $moveAppInfo
})
}
.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.PERCENTAGE_15)
if (this.moveAppShow) {
Stack() {
Column() {
AppIcon({
iconSize: this.AppItemWidth,
appIcon: this.moveAppInfo.AppIcon,
bundleName: this.moveAppInfo.bundleName,
labelId: this.moveAppInfo.labelId
})
}
}
.height(this.AppItemHeight)
.width(this.AppItemWidth)
.position({
x: this.moveAppX - (DefaultConstants.DEFAULT_APP_ITEM_WIDTH / 2),
y: this.moveAppY - (DefaultConstants.DEFAULT_APP_ITEM_WIDTH / 2)
})
}
}
.gesture(
LongPressGesture({ repeat: false })
.onAction((event: GestureEvent) => {
mAppGridPresenter.onPageLongPress();
this.dialogController.open();
})
)
.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.PERCENTAGE_100)
}
}
@Component
struct GridSwiper {
@Link moveAppX: number;
@Link moveAppY: number;
@Link moveAppShow: boolean;
@Link moveAppInfo: any;
@StorageLink('pageIndex') PageIndex: number = 0;
private mAppGridInfo;
private mAppBottomBarInfo;
private aboutToAppear(): void {
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Swiper() {
ForEach(this.mAppGridInfo, (item) => {
SwiperPage({
mAppListInfo: item,
mAppBottomBarInfo: this.mAppBottomBarInfo,
moveAppX: $moveAppX,
moveAppY: $moveAppY,
moveAppShow: $moveAppShow,
moveAppInfo: $moveAppInfo
})
})
}
.height(DefaultConstants.PERCENTAGE_100)
.width(DefaultConstants.PERCENTAGE_100)
.loop(false)
.index(this.PageIndex)
.onChange((index) => {
mAppGridPresenter.changeIndex(index);
})
}
.height(DefaultConstants.PERCENTAGE_100)
.width(DefaultConstants.PERCENTAGE_100)
}
}
@Component
struct SwiperPage {
@State ColumnsTemplate: string = '';
@State RowsTemplate: string = ''
@Link moveAppX: number;
@Link moveAppY: number;
@Link moveAppShow: boolean;
@Link moveAppInfo: any;
private mAppBottomBarInfo;
private mAppListInfo;
private aboutToAppear(): void {
let mGridConfig = mAppGridPresenter.getGridConfig();
let column = mGridConfig.column;
let row = mGridConfig.row;
for (let i = 0;i < column; i++) {
this.ColumnsTemplate += '1fr '
}
for (let i = 0;i < row; i++) {
this.RowsTemplate += '1fr '
}
}
build() {
Grid() {
ForEach(this.mAppListInfo, (item) => {
GridItem() {
AppItem({
mAppBottomBarInfo: this.mAppBottomBarInfo
item: item,
moveAppX: $moveAppX,
moveAppY: $moveAppY,
moveAppShow: $moveAppShow,
moveAppInfo: $moveAppInfo
})
}
.rowStart(item.row)
.columnStart(item.column)
.rowEnd(item.row)
.columnEnd(item.column)
})
}
.columnsTemplate(this.ColumnsTemplate)
.rowsTemplate(this.RowsTemplate)
}
}
@Component
struct AppItem {
@State AppItemHeight: number = DefaultConstants.DEFAULT_APP_ITEM_HEIGHT;
@State AppItemWidth: number = DefaultConstants.DEFAULT_APP_ITEM_WIDTH;
@State AppNameSize: number = DefaultConstants.DEFAULT_APP_NAME_SIZE;
@State isShow: boolean = true;
@State isLongPress: boolean = false;
@Link moveAppX: number;
@Link moveAppY: number;
@Link moveAppShow: boolean;
@Link moveAppInfo: any;
private mAppBottomBarInfo;
private startPosition;
private endPosition;
private item: any;
private isSwappingPage = false;
dialogController: CustomDialogController = new CustomDialogController({
builder: UninstallDialog(),
cancel: this.cancelDialog,
autoCancel: true
});
cancelDialog() {
console.info('Launcher Grid Cancel Dialog');
}
movingIconSwapPageDelay() {
this.isSwappingPage = true;
setTimeout(() => {
this.isSwappingPage = false;
}, APP_INFO_REFRESH_DELAY);
}
build() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceAround
}) {
if (this.isShow) {
Column() {
AppIcon({
iconSize: this.AppItemWidth,
appIcon: this.item.AppIcon,
bundleName: this.item.bundleName,
labelId: this.item.labelId
})
GridName({
nameHeight: this.AppItemHeight - this.AppItemWidth,
nameWidth: this.AppItemWidth,
appName: this.item.AppName,
nameSize: this.AppNameSize,
bundleName: this.item.bundleName,
labelId: this.item.labelId
})
}
.onClick(() => {
mAppGridPresenter.openApplication(this.item.abilityName, this.item.bundleName);
})
}
}
.gesture(
LongPressGesture({ repeat: false })
.onAction((event: GestureEvent) => {
this.item.AppName = mAppGridPresenter.getAppName(this.item.bundleName);
AppStorage.SetOrCreate('uninstallAppInfo', this.item)
this.dialogController.open();
this.isLongPress = true;
this.moveAppShow = true;
this.isShow = false;
})
)
.onTouch((event: TouchEvent) => {
if (event.type == 0) {
this.moveAppX = event.touches[0].screenX;
this.moveAppY = event.touches[0].screenY;
this.moveAppInfo = this.item;
this.startPosition = mAppGridPresenter.getTouchPosition(event.touches[0].screenX * mScreenMagnification, event.touches[0].screenY * mScreenMagnification);
}
if (event.type == 1 && this.isLongPress) {
this.moveAppX = event.touches[0].screenX;
this.moveAppY = event.touches[0].screenY;
this.moveAppShow = false;
this.isShow = true;
this.isLongPress = false;
this.endPosition = mAppGridPresenter.getTouchPosition(event.touches[0].screenX * mScreenMagnification, event.touches[0].screenY * mScreenMagnification);
mAppGridPresenter.layoutAdjustment(this.startPosition, this.endPosition, this.mAppBottomBarInfo);
}
if (event.type == 2 && this.isLongPress) {
this.dialogController.close();
this.moveAppX = event.touches[0].screenX;
this.moveAppY = event.touches[0].screenY;
let moveX = this.moveAppX * mScreenMagnification;
let moveY = this.moveAppY * mScreenMagnification;
if ((moveX - CELLWIDTH / 2) < 0 && !this.isSwappingPage && mAppGridPresenter.getIndex() > 0 && moveY < mSHeight) {
mAppGridPresenter.changeIndex(mAppGridPresenter.getIndex() - 1);
this.movingIconSwapPageDelay();
} else if ((moveX + CELLWIDTH / 2) > DESIGN_WIDTH && !this.isSwappingPage && moveY < mSHeight) {
if (mAppGridPresenter.getIndex() == mAppGridPresenter.getGridPageCount() - 1) {
mAppGridPresenter.addBlankPage();
} else {
mAppGridPresenter.changeIndex(mAppGridPresenter.getIndex() + 1);
}
this.movingIconSwapPageDelay();
}
}
})
.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.PERCENTAGE_100)
}
}
@Component
struct BottomBar {
@State AppItemHeight: number = DefaultConstants.DEFAULT_APP_ITEM_HEIGHT;
@State AppItemWidth: number = DefaultConstants.DEFAULT_APP_ITEM_WIDTH;
@State AppNameSize: number = DefaultConstants.DEFAULT_APP_NAME_SIZE;
@StorageLink('BottomBarItemWidth') ItemWidth: string = DefaultConstants.PERCENTAGE_25;
@Link moveAppX: number;
@Link moveAppY: number;
@Link moveAppShow: boolean;
@Link moveAppInfo: any;
private mAppBottomBarInfo;
private aboutToAppear(): void {
}
build() {
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceAround }) {
Row() {
ForEach(this.mAppBottomBarInfo, (item) => {
Column() {
AppItem({
mAppBottomBarInfo: this.mAppBottomBarInfo
item: item,
moveAppX: $moveAppX,
moveAppY: $moveAppY,
moveAppShow: $moveAppShow,
moveAppInfo: $moveAppInfo
})
}
.height(DefaultConstants.PERCENTAGE_100)
.width(this.ItemWidth)
})
}
.height(DefaultConstants.PERCENTAGE_100)
}
.height(DefaultConstants.PERCENTAGE_100)
.width(DefaultConstants.PERCENTAGE_100)
}
}
@CustomDialog
struct ShowDialog {
controller: CustomDialogController;
cancel: () => void;
action: () => void;
@StorageLink('blankPageBtnText') buttonText: string = '';
build() {
Row() {
Button() {
Text($r('app.string.intoSettings'))
.fontSize(15)
.fontColor(Color.White)
}
.width('200px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppGridPresenter.intoSetting();
this.controller.close();
})
Button(this.buttonText)
.borderRadius('25px')
.width('200px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppGridPresenter.addOrDeleteBlankPage();
this.controller.close();
})
}.padding(20)
}
}
@CustomDialog
struct UninstallDialog {
controller: CustomDialogController;
cancel: () => void;
action: () => void;
@StorageLink('uninstallAppInfo') AppInfo: any = {};
build() {
Column() {
Row() {
Text($r('app.string.uninstall'))
Text(this.AppInfo.AppName + ' ?')
}
Row() {
Button() {
Text($r('app.string.submit'))
.fontSize(25)
}
.margin({ right: 20 })
.width('150px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppGridPresenter.uninstallApp(this.AppInfo.bundleName, this.AppInfo.System);
this.controller.close();
})
Button() {
Text($r('app.string.cancel'))
.fontSize(25)
}
.margin({ left: 20 })
.width('150px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
this.controller.close();
})
}
.margin({ top: 20 })
.padding(20)
}
.padding(20)
}
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import AppListPresenter from '../common/presenter/AppListPresenter.ets'
import ListName from '../../../../../../../common/src/main/ets/default/uicomponents/ListName.ets'
import AppIcon from '../../../../../../../common/src/main/ets/default/uicomponents/AppIcon.ets'
import DefaultConstants from '../common/constants/DefaultConstants.ets'
let mAppListPresenter: AppListPresenter;
@Component
export default
struct ListLayout {
@StorageLink('listInfo') AppListInfo: [] = [];
private aboutToAppear(): void {
mAppListPresenter = AppListPresenter.getInstance();
mAppListPresenter.getAppList();
mAppListPresenter.registerAppListChangeCallback()
}
dialogController: CustomDialogController = new CustomDialogController({
builder: ShowDialog({action: this.openUninstallDialog}),
cancel: this.cancelDialog,
autoCancel: true
});
uninstallDialogController: CustomDialogController = new CustomDialogController({
builder: UninstallDialog(),
cancel: this.cancelDialog,
autoCancel: true
});
openUninstallDialog() {
this.dialogController.close();
this.uninstallDialogController.open();
}
cancelDialog() {
console.info('Launcher Grid Cancel Dialog');
}
build() {
List({ space: 5, initialIndex: 1 }) {
ForEach(this.AppListInfo, (item) => {
ListItem() {
Row() {
AppIcon({
iconSize: DefaultConstants.DEFAULT_APP_ITEM_WIDTH,
appIcon: item.AppIcon,
bundleName: item.bundleName,
labelId: item.labelId
})
ListName({
appName: item.AppName,
nameSize: DefaultConstants.DEFAULT_APP_NAME_SIZE,
bundleName: item.bundleName,
labelId: item.labelId
})
}
.gesture(
LongPressGesture({ repeat: false })
.onAction((event: GestureEvent) => {
AppStorage.SetOrCreate('uninstallAppInfo', item);
this.dialogController.open();
})
)
.width(DefaultConstants.PERCENTAGE_100)
.height(80)
.backgroundColor('#3c000000')
.borderRadius(20)
.padding({ left: 5 })
.margin({ top: 5, left: 30, bottom: 5, right: 30 })
}
})
}
.gesture(
LongPressGesture({ repeat: false })
.onAction((event: GestureEvent) => {
mAppListPresenter.intoSetting();
})
)
.width('100%')
.height('100%')
}
}
@CustomDialog
struct ShowDialog {
@StorageLink('uninstallAppInfo') AppInfo: any = {};
controller: CustomDialogController;
cancel: () => void;
action: () => void;
build() {
Row() {
Button() {
Text($r('app.string.intoSettings'))
.fontColor(Color.White)
.fontSize(15)
}
.width('200px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppListPresenter.intoSetting();
this.controller.close();
})
Button() {
Text($r('app.string.uninstall'))
.fontColor(Color.White)
.fontSize(15)
}
.width('200px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppListPresenter.uninstallApp(this.AppInfo.bundleName, this.AppInfo.System);
this.controller.close();
})
}.padding(20)
}
}
@CustomDialog
struct UninstallDialog {
controller: CustomDialogController;
cancel: () => void;
action: () => void;
@StorageLink('uninstallAppInfo') AppInfo: any = {};
build() {
Column() {
Row() {
Text($r('app.string.uninstall'))
Text(this.AppInfo.AppName + ' ?')
}
Row() {
Button() {
Text($r('app.string.submit'))
.fontColor(Color.White)
.fontSize(15)
}
.margin({ right: 20 })
.width('150px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
mAppListPresenter.uninstallApp(this.AppInfo.bundleName, this.AppInfo.System);
this.controller.close();
})
Button() {
Text($r('app.string.cancel'))
.fontColor(Color.White)
.fontSize(15)
}
.margin({ left: 20 })
.width('150px')
.height('50px')
.backgroundColor(Color.Blue)
.onClick(() => {
this.controller.close();
})
}
.margin({ top: 20 })
.padding(20)
}.padding(20)
}
}

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "layoutmanager_MainAbility",
"value": "layoutmanager_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "intoSettings",
"value": "Launcher settings"
},
{
"name": "addBlankPage",
"value": "Add Blank Page"
},
{
"name": "deleteBlankPage",
"value": "Delete Blank Page"
},
{
"name": "uninstall",
"value": "Uninstall"
},
{
"name": "submit",
"value": "Submit"
},
{
"name": "cancel",
"value": "Cancel"
}
]
}

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "layoutmanager_MainAbility",
"value": "layoutmanager_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "intoSettings",
"value": "Launcher settings"
},
{
"name": "addBlankPage",
"value": "Add Blank Page"
},
{
"name": "deleteBlankPage",
"value": "Delete Blank Page"
},
{
"name": "uninstall",
"value": "Uninstall"
},
{
"name": "submit",
"value": "Submit"
},
{
"name": "cancel",
"value": "Cancel"
}
]
}

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "layoutmanager_MainAbility",
"value": "layoutmanager_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "intoSettings",
"value": "桌面设置"
},
{
"name": "addBlankPage",
"value": "添加空白页"
},
{
"name": "deleteBlankPage",
"value": "删除空白页"
},
{
"name": "uninstall",
"value": "卸载"
},
{
"name": "submit",
"value": "确认"
},
{
"name": "cancel",
"value": "取消"
}
]
}

View File

@ -0,0 +1,21 @@
apply plugin: 'com.huawei.ohos.hap'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 6
defaultConfig {
compatibleSdkVersion 4
}
supportSystem "standard"
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
entryModules "phone"
}
dependencies {
entryImplementation project(':phone')
}

View File

@ -5,17 +5,13 @@
"version": {
"code": 1000000,
"name": "1.0.0"
},
"apiVersion": {
"compatible": 5,
"target": 5,
"releaseType": "Beta1"
}
},
"deviceConfig": {},
"module": {
"package": "com.ohos.launcher.recents",
"name": ".MyApplication",
"mainAbility": "com.ohos.launcher.recents.MainAbility",
"deviceType": [
"phone"
],
@ -28,10 +24,10 @@
"abilities": [
{
"visible": true,
"name": "com.ohos.launcher.recents.MainAbility",
"name": "com.ohos.launcher.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:app_name",
"label": "$string:recents_MainAbility",
"type": "page",
"launchType": "singleton",
"metaData": {
@ -45,8 +41,12 @@
],
"js": [
{
"mode": {
"syntax": "ets",
"type": "pageAbility"
},
"pages": [
"pages/recent/recent"
"pages/recents"
],
"name": "default",
"window": {

View File

@ -12,9 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../css/CommonPageStyle.css';
.app-name {
color: white;
export default {
onCreate() {
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import NapiAbilityManager from '@ohos.app.abilitymanager';
import Storage from '@ohos.data.storage';
import BundleMgr from '@ohos.bundle';
import FeatureAbility from '@ohos.ability.featureability';
const PREFERENCES_PATH: string = '/data/accounts/account_0/appdata/com.ohos.launcher/sharedPreference/LauncherPreference';
const MAX_NUM: number = 20;
const PERMISSION_NUM: number = 8;
const NON: number = 0;
const RECENT_PROCESS_LIMIT_KEY: string = 'RecentProcessLimit';
const DEFAULT_RECENT_PROCESS_LIMIT: number = 10;
let mRecentList = [];
let mIconResultCount: number = 0;
let mPreferences: any = Storage.getStorageSync(PREFERENCES_PATH);
/**
* Class RecentsModel.
*/
export default class RecentsModel {
private static mRecentsModel = new RecentsModel();
constructor() {
}
/**
* Return an instance of RecentsModel.
*/
public static getInstance() {
return this.mRecentsModel;
}
/**
* Get recent process list.
*
* @param {object} callback - The callback from presenter.
*/
getRecentProcessList(callback) {
console.info("Launcher recents RecentsModel getRecentProcessList start");
console.info('Launcher recents getRecentProcessList mRecentList first= ' + JSON.stringify(mRecentList));
mRecentList = [];
mIconResultCount = 0;
console.info("Launcher recents RecentsModel NapiAbilityManager.queryRecentAbilityMissionInfos start");
NapiAbilityManager.queryRunningAbilityMissionInfos(MAX_NUM)
.then((data) => {
console.info("Launcher recents RecentsModel NapiAbilityManager.queryRecentAbilityMissionInfos() callback");
console.info('Launcher recents queryRecentAbilityMissionInfos data length [' + data.length + ']');
console.info('Launcher recents queryRecentAbilityMissionInfos data = ' + JSON.stringify(data));
if (data.length === 0) {
console.info('Launcher recents data empty');
callback(mRecentList);
return;
}
for (let i = 0; i < data.length; i++) {
let recentTaskInfo =
{
AppName: data[i].missionDescription.label,
AppId: data[i].topAbility.bundleName,
bundleName: data[i].topAbility.bundleName,
abilityName: data[i].topAbility.abilityName,
iconId: data[i].missionDescription.iconPath,
labelId: '',
missionId: data[i].id
}
mRecentList.push(recentTaskInfo);
}
console.info('Launcher recents RecentsModel queryRecentAbilityMissionInfos mRecentList = ' + JSON.stringify(mRecentList));
AppStorage.SetOrCreate('recentProcessList', mRecentList);
callback(mRecentList);
})
.catch(error =>
console.error("Launcher recents RecentsModel getRecentProcessList promise::catch : " + JSON.stringify(error))
);
console.info("Launcher recents RecentsModel getRecentProcessList end");
}
/**
* Clear recent process list.
*/
clearRecentProcess() {
console.info("Launcher recents RecentsModel clearRecentProcess start");
while (mRecentList.length > 0) {
mRecentList.pop();
}
console.info("Launcher recents RecentsModel mRecentList: " + mRecentList.length + JSON.stringify(mRecentList));
NapiAbilityManager.clearMissions((data) => {
});
AppStorage.SetOrCreate('recentProcessList', mRecentList);
console.info("Launcher recents RecentsModel clearRecentProcess end");
}
/**
* Remove recent process list.
*
* @param {string} missionId - The missionId of recent process.
*/
removeRecentProcess(missionId) {
console.info("Launcher recents RecentsModel removeRecentProcess start");
console.info('Launcher recents removeRecentProcess missionId ' + missionId);
for (let idx = 0; idx < mRecentList.length; idx++) {
if (mRecentList[idx].missionId === missionId) {
mRecentList.splice(idx, 1);
break;
}
}
console.info("Launcher recents RecentsModel removeRecentProcess mRecentList" + JSON.stringify(mRecentList));
NapiAbilityManager.removeMission(missionId, (err, data) => {
console.info('removeMission data [' + data + ']');
});
if (mRecentList.length === 0) {
console.info("Launcher recents RecentsModel feature_ability.terminateAbility start")
}
AppStorage.SetOrCreate('recentProcessList', mRecentList);
console.info("Launcher recents RecentsModel removeRecentProcess end");
}
/**
* Get recent process list.
*
* @return {number} - The number of recent process.
*/
public getRecentProcessLimit() {
console.info("Launcher recents RecentsModel getRecentProcessLimit start");
let limit: number = DEFAULT_RECENT_PROCESS_LIMIT;
if (mPreferences != null && mPreferences != undefined) {
limit = mPreferences.getSync(RECENT_PROCESS_LIMIT_KEY, DEFAULT_RECENT_PROCESS_LIMIT);
}
console.info("Launcher recents RecentsModel getRecentProcessLimit end limit = " + limit);
return limit;
}
/**
* Start ability.
*
* @param {object} appInfo - The app info.
*/
startAbility(appInfo) {
// promise
console.info('Launcher startApplication abilityname');
let result = FeatureAbility.startAbility({
want: {
bundleName: appInfo.AppId,
abilityName: appInfo.abilityName
}
})
.then(data =>
console.info("Launcher promise::then : " + JSON.stringify(data))
)
.catch(error =>
console.info("Launcher promise::catch : " + JSON.stringify(error))
);
console.info("Launcher AceApplication : startAbility : " + result);
}
}

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 239 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Router from '@system.router';
import RecentsModel from '../model/RecentsModel.ets';
let mRecentsModel: RecentsModel = RecentsModel.getInstance();
let mRecentsLimit: number = mRecentsModel.getRecentProcessLimit();
let recentProcessList = [];
/**
* Class RecentsPresenter.
*/
export default class RecentsPresenter {
private static recentsPresenter: RecentsPresenter = new RecentsPresenter();
/**
* Return an instance of RecentsPresenter.
*/
public static getInstance(): RecentsPresenter{
return this.recentsPresenter;
}
/**
* Callback function of getRecentProcessList.
*/
getRecentProcessList() {
console.info("Launcher recents RecentsPresenter getRecentProcessList start");
mRecentsModel.getRecentProcessList((data) => {
let recentProcessList = data;
if (recentProcessList.length > mRecentsLimit) {
recentProcessList.splice(0, recentProcessList.length - mRecentsLimit);
}
console.info("Launcher recents RecentsPresenter mRecentsModel.getRecentProcessList getRecentProcessList recentProcessList = " + JSON.stringify(recentProcessList));
});
console.info("Launcher recents RecentsPresenter getRecentProcessList end")
}
/**
* Clear recent process.
*/
clearRecentProcess() {
console.info("Launcher recents RecentsPresenter clearRecentProcess start");
mRecentsModel.clearRecentProcess();
console.log("Launcher recents RecentsPresenter clearRecentProcess recentProcessList" + JSON.stringify(recentProcessList));
console.info("Launcher recents RecentsPresenter clearRecentProcess end");
}
/**
* Remove recent process.
*
* @param {string} missionId - The missionId of recent process.
*/
removeRecentProcess(missionId) {
console.info("Launcher recents RecentsPresenter removeRecentProcess start missionId = " + missionId);
mRecentsModel.removeRecentProcess(missionId);
console.info("Launcher recents RecentsPresenter removeRecentProcess end");
}
/**
* Back to desktop.
*/
back() {
console.info("Launcher recents RecentsPresenter back start");
Router.back();
console.info("Launcher recents RecentsPresenter back end");
}
/**
* Start app.
*
* @param {object} appInfo - The app info.
*/
startUpApp(appInfo) {
console.info("Launcher recents RecentsPresenter startUpApp start appInfo = " + JSON.stringify(appInfo));
mRecentsModel.startAbility(appInfo);
console.info("Launcher recents RecentsPresenter startUpApp end ");
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import GridName from '../../../../../../../common/src/main/ets/default/uicomponents/GridName.ets'
import AppIcon from '../../../../../../../common/src/main/ets/default/uicomponents/AppIcon.ets'
import DefaultConstants from '../../../../../../../common/src/main/ets/default/common/constants/DefaultConstants.ets'
import RecentsPresenter from '../common/presenter/RecentsPresenter.ets';
let mRecentsPresenter: RecentsPresenter;
@Entry
@Component
struct Recent {
@StorageLink('recentProcessList') mRecentProcessList: [] = []
@State size: string = '100%'
aboutToAppear() {
console.log("Launcher recents aboutToAppear start");
mRecentsPresenter = RecentsPresenter.getInstance()
console.log("Launcher recents aboutToAppear end");
}
onPageShow() {
console.log("Launcher recents onPageShow start");
mRecentsPresenter.getRecentProcessList();
console.log("Launcher recents onPageShow end");
}
onPageHide() {
console.info("Launcher recents onPageHide start");
this.mRecentProcessList = []
console.info("Launcher recents onPageHide end");
}
build() {
Column() {
if (this.mRecentProcessList.length === 0) {
emptyMsgDisplay()
}
recentProcessListDisplay({ recentProcessList: $mRecentProcessList })
}
.width(this.size)
.height(this.size)
// .backgroundImage(DefaultConstants.DEFAULT_BACKGROUND_IMAGE_RECENT)
.backgroundImage($m('app.media.ic_wallpaper_recent.jpg'))
}
}
@Component
struct recentProcessListDisplay {
@Link recentProcessList: []
scroller: Scroller = new Scroller()
@State AppIconSize: number = 55
@State AppItemHeight: number = DefaultConstants.DEFAULT_APP_ITEM_HEIGHT;
@State AppItemWidth: number = DefaultConstants.DEFAULT_APP_ITEM_WIDTH;
@State appHeightSize: string = '85%'
@State deleteHeightSize: string = '15%'
@State AppborderRadius: number = 10
@State DeleteImageSize: number = 50
@State DeleteOpacity: number = 0.5
@State margin: number = 10
build() {
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start, justifyContent: FlexAlign.Center }) {
Scroll(this.scroller) {
Row() {
ForEach(this.recentProcessList, (item) => {
Column() {
Column() {
AppIcon({
iconSize: this.AppIconSize,
appIcon: item.iconId,
bundleName: item.bundleName,
labelId: item.labelId
})
GridName({
appName: item.AppName,
nameHeight: this.AppItemHeight - this.AppItemWidth,
nameWidth: this.AppItemWidth,
bundleName: item.bundleName,
labelId: item.labelId
})
}
.margin({ bottom: this.margin })
Image(DefaultConstants.DEFAULT_APP_IMAGE)
.borderRadius(this.AppborderRadius)
.width('100%')
.height(DefaultConstants.APP_IMAGE_HEIGHT)
}
.width(DefaultConstants.APP_IMAGE_WIDTH)
.height('100%')
.margin({ right: this.margin })
.onClick(() => {
mRecentsPresenter.startUpApp(item)
})
.gesture(
PanGesture({ fingers: 1, direction: PanDirection.Vertical, distance: 5 }) //
.onActionEnd((e) => {
let mOffectWidth = (DefaultConstants.APP_IMAGE_WIDTH) / 2;
console.log('e.offsetX, Y' + e.offsetX + e.offsetY)
if (e.offsetY < -50 && e.offsetX <= mOffectWidth && -mOffectWidth <= e.offsetX) {
mRecentsPresenter.removeRecentProcess(item.missionId);
}
}))
})
}
}.height(this.appHeightSize)
.scrollable(ScrollDirection.Horizontal)
}
.width('100%')
.height(this.appHeightSize)
Column() {
Image(DefaultConstants.DEFAULT_DELETE_IMAGE)
.width(this.DeleteImageSize)
.height(this.DeleteImageSize)
.opacity(this.DeleteOpacity)
.align(Alignment.Bottom)
.onClick(() => {
mRecentsPresenter.clearRecentProcess();
}))
}.alignItems(HorizontalAlign.Center)
.width('100%')
.height(this.deleteHeightSize)
}
}
}
@Component
struct emptyMsgDisplay {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text($r('app.string.No_running_apps_recently'))
.fontColor(DefaultConstants.DEFAULT_FONT_COLOR)
.fontSize(20)
}
.width('100%')
.height('100%')
}
}

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "recents_MainAbility",
"value": "recents_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "No_running_apps_recently",
"value": "No running apps recently"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "recents_MainAbility",
"value": "recents_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "No_running_apps_recently",
"value": "No running apps recently"
}
]
}

View File

@ -0,0 +1,16 @@
{
"string": [
{
"name": "recents_MainAbility",
"value": "recents_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "No_running_apps_recently",
"value": "最近无运行应用"
}
]
}

View File

@ -1,8 +1,10 @@
apply plugin: 'com.huawei.ohos.hap'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 6
defaultConfig {
compatibleSdkVersion 6
compatibleSdkVersion 4
}
supportSystem "standard"
buildTypes {
@ -13,10 +15,9 @@ ohos {
}
}
}
entryModules "launcher"
entryModules "phone"
}
dependencies {
entryImplementation project(':launcher')
entryImplementation project(':phone')
}

View File

@ -5,11 +5,6 @@
"version": {
"code": 1000000,
"name": "1.0.0"
},
"apiVersion": {
"compatible": 5,
"target": 5,
"releaseType": "Beta1"
}
},
"deviceConfig": {},
@ -32,21 +27,25 @@
"name": "com.ohos.launcher.settings.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:app_name",
"label": "$string:settings_MainAbility",
"type": "page",
"launchType": "singleton"
}
],
"js": [
{
"mode": {
"syntax": "ets",
"type": "pageAbility"
},
"pages": [
"pages/Settings"
],
"name": "default",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"pages": [
"pages/settings/settings"
]
"autoDesignWidth": false
}
}
]
}

View File

@ -12,13 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../css/CommonPageStyle.css';
.image-box{
display: flex;
align-items: center;
justify-content: center;
export default {
onCreate() {
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default class StyleConstants {
public static DEFAULT_LAYOUT_FONT_COLOR: string = '#696969';
public static DEFAULT_BACKGROUND_COLOR: string = '#F0EEEE';
public static DEFAULT_DIALOG_FONT_COLOR: string = '#000000';
public static DEFAULT_SETTING_PAGE_COLOR: string = '#FFFFFF';
public static PERCENTAGE_100: string = '100%';
public static PERCENTAGE_90: string = '90%';
public static PERCENTAGE_85: string = '85%';
public static PERCENTAGE_60: string = '60%';
public static PERCENTAGE_30: string = '30%';
public static PERCENTAGE_15: string = '15%';
public static DEFAULT_APP_NAME_SIZE: number= 20;
public static DEFAULT_APP_ITEM_WIDTH: number = 70;
public static DEFAULT_APP_ITEM_HEIGHT: number = 95;
public static DEFAULT_PIXEL_10: number = 10;
public static DEFAULT_PIXEL_15: number = 15;
public static DEFAULT_PIXEL_20: number = 20;
public static DEFAULT_PIXEL_25: number = 25;
public static DEFAULT_PIXEL_30: number = 30;
public static DEFAULT_PIXEL_35: number = 35;
public static DEFAULT_PIXEL_40: number = 40;
public static DEFAULT_PIXEL_80: number = 80;
public static DEFAULT_PIXEL_60: number = 60;
public static DEFAULT_PIXEL_75: number = 75;
public static DEFAULT_PIXEL_100: number = 100;
public static DEFAULT_PIXEL_120: number = 120;
public static DEFAULT_PIXEL_200: number = 200;
public static DEFAULT_PIXEL_340: number = 340;
}

View File

@ -0,0 +1,194 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import DefaultLayoutConfig from '../../../../../../../../common/src/main/ets/default/common/configs/DefaultLayoutConfig.ets'
import GridLayoutConfigs from '../../../../../../../../common/src/main/ets/default/common/configs/GridLayoutConfigs.ets'
import SettingsModel from '../../../../../../../../common/src/main/ets/default/common/model/SettingsModel.ets'
let mSettingsModel: SettingsModel;
/**
* Class SettingsPresenter.
*/
export default class SettingsPresenter {
private static settingPresenter: SettingsPresenter = new SettingsPresenter();
private callbackList = [];
/**
* Grid settingList.
*
*/
private gridSettingsList = [
{
ida: 0,
settingName: $r('app.string.layoutStyle'),
settingValue: '',
valueList: DefaultLayoutConfig.DefaultLayoutOptions
},
{
ida: 1,
settingName: $r('app.string.launcherLayout'),
settingValue: '',
valueList: GridLayoutConfigs.GridLayoutTable
},
{
ida: 2,
settingName: $r('app.string.recentTasksSetting'),
settingValue: '',
valueList: DefaultLayoutConfig.DefaultRecentProcessLimitArray
}
]
/**
* List settingList.
*
*/
private listSettingsList = [
{
ida: 0,
settingName: $r('app.string.layoutStyle'),
settingValue: '',
valueList: DefaultLayoutConfig.DefaultLayoutOptions
},
{
ida: 2,
settingName: $r('app.string.recentTasksSetting'),
settingValue: '',
valueList: DefaultLayoutConfig.DefaultRecentProcessLimitArray
}
]
/**
* Constructor.
*
* @param {object} settingsModel - model of setting.
*/
constructor() {
mSettingsModel = new SettingsModel();
}
/**
* Get settingsPresenter instance.
*
* @return {settingPresenter} - settingPresenter.
*/
public static getInstance(): SettingsPresenter{
return this.settingPresenter;
}
/**
* Get setting list.
*
* @return [settingList] - setting list.
*/
public getSettingList() {
let layout = mSettingsModel.getAppPageStartConfig();
if (layout == 'Grid') {
this.gridSettingsList[0].settingValue = layout;
this.gridSettingsList[1].settingValue = mSettingsModel.getGridConfig().layout;
this.gridSettingsList[2].settingValue = mSettingsModel.getRecentProcessLimit().toString();
return this.gridSettingsList;
} else {
this.listSettingsList[0].settingValue = layout;
this.listSettingsList[1].settingValue = mSettingsModel.getRecentProcessLimit().toString();
return this.listSettingsList;
}
}
/**
* Set system setting value.
*
* @param {string} settingsName - setting name.
* @param {string} settingValue - setting value.
*/
setSettingsValue(ida, settingValue) {
if (ida == 0) {
this.setAppPageStartConfig(settingValue);
} else if (ida == 1) {
this.setGridConfig(settingValue);
} else {
this.setRecentProcessLimit(settingValue);
}
this.settingUpdate();
}
/**
* Set app start config.
*
* @param {string} type - the type of config.
*/
setAppPageStartConfig(type) {
mSettingsModel.setAppPageStartConfig(type);
}
/**
* Update setting.
*
*/
settingUpdate() {
mSettingsModel.closeSettings();
}
/**
* Set grid config.
*
* @param {string} id - the id of grid config.
*/
setGridConfig(id) {
mSettingsModel.setGridConfig(id);
}
/**
* Set recent process.
*
* @param {number} num - the num of recent process.
*/
setRecentProcessLimit(num) {
mSettingsModel.setRecentProcessLimit(num);
}
/**
* Back to the desktop interface.
*
*/
backToTheDesktop() {
this.settingUpdate();
}
/**
* Register value callback.
*
* @param {string} settingsName - setting name.
* @param {function()} settingValue - setting value.
*/
registerValueCallback(ida, settingValue) {
this.callbackList.push({
id: ida,
fun: settingValue
});
}
/**
* Change page setting value.
*
* @param {string} settingsName - setting name.
* @param {string} settingValue - setting value.
*/
changeSettingValue(ida, settingValue) {
for (let i = 0;i < this.callbackList.length; i++) {
if (this.callbackList[i].id == ida) {
this.callbackList[i].fun(settingValue);
break;
}
}
}
}

View File

@ -0,0 +1,213 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import DefaultConstants from '../common/constants/DefaultConstants.ets'
import SettingsPresenter from '../common/presenter/SettingsPresenter.ets'
let mSettingsPresenter: SettingsPresenter;
@Entry
@Component
struct Index {
private aboutToAppear(): void {
mSettingsPresenter = SettingsPresenter.getInstance();
}
build() {
Flex({ alignItems: ItemAlign.Center, direction: FlexDirection.Column }) {
Row() {
top_bar()
}
Column() {
Text($r('app.string.layout'))
.fontSize(DefaultConstants.DEFAULT_PIXEL_25)
.fontColor(DefaultConstants.DEFAULT_LAYOUT_FONT_COLOR)
.margin({ right: DefaultConstants.DEFAULT_PIXEL_340 })
}
Column() {
SettingPage()
}
}.width(DefaultConstants.PERCENTAGE_100)
.backgroundColor(DefaultConstants.DEFAULT_BACKGROUND_COLOR)
}
}
@Component
struct top_bar {
build() {
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
Image($r('app.media.ic_back'))
.margin({ right: DefaultConstants.DEFAULT_PIXEL_20,
left: DefaultConstants.DEFAULT_PIXEL_20 })
.width(DefaultConstants.DEFAULT_PIXEL_75)
.height(DefaultConstants.DEFAULT_PIXEL_75)
.objectFit(ImageFit.Contain)
.onClick(() => {
mSettingsPresenter.backToTheDesktop();
})
Text($r('app.string.intoSettings'))
.fontSize(DefaultConstants.DEFAULT_PIXEL_35)
}
.margin({ top: DefaultConstants.DEFAULT_PIXEL_10 })
.height(DefaultConstants.DEFAULT_PIXEL_80)
.width(DefaultConstants.PERCENTAGE_90)
.backgroundColor(DefaultConstants.DEFAULT_BACKGROUND_COLOR)
.padding({ bottom: DefaultConstants.DEFAULT_PIXEL_10 })
}
}
@Component
struct SettingPage {
@State SettingList: any = [];
private aboutToAppear(): void {
this.SettingList = mSettingsPresenter.getSettingList();
}
private onPageShow(): void {
this.SettingList = mSettingsPresenter.getSettingList();
}
build() {
Column() {
ForEach(this.SettingList, (item: any) => {
SettingItem({
ida: item.ida,
settingName: item.settingName,
settingValue: item.settingValue,
valueList: item.valueList,
})
})
}
.margin({ top: DefaultConstants.DEFAULT_PIXEL_10,
bottom: DefaultConstants.DEFAULT_PIXEL_10,
left: DefaultConstants.DEFAULT_PIXEL_30,
right: DefaultConstants.DEFAULT_PIXEL_30 })
.height(DefaultConstants.PERCENTAGE_100)
.padding(DefaultConstants.DEFAULT_PIXEL_20)
.borderRadius(DefaultConstants.DEFAULT_PIXEL_30)
.backgroundColor(DefaultConstants.DEFAULT_SETTING_PAGE_COLOR)
}
}
@Component
struct SettingItem {
@State ida: number = 0;
@State settingValue: string = ' ';
@State settingName: string = ' ';
private valueList: any;
dialogController: CustomDialogController = new CustomDialogController({
builder: SettingsDialog(),
cancel: this.cancelDialog,
autoCancel: true
});
cancelDialog() {
console.info('cancelDialog');
}
private aboutToAppear(): void {
mSettingsPresenter.registerValueCallback(this.ida, this.callback.bind(this));
}
callback(data) {
this.settingValue = data;
}
build() {
Column() {
Row() {
Text(this.settingName)
.lineHeight(DefaultConstants.DEFAULT_PIXEL_60)
.height(DefaultConstants.DEFAULT_PIXEL_60)
.fontSize(DefaultConstants.DEFAULT_PIXEL_30)
.width(DefaultConstants.PERCENTAGE_60)
.align(Alignment.Start)
Text(this.settingValue)
.lineHeight(DefaultConstants.DEFAULT_PIXEL_60)
.height(DefaultConstants.DEFAULT_PIXEL_60)
.fontSize(DefaultConstants.DEFAULT_PIXEL_30)
.width(DefaultConstants.DEFAULT_PIXEL_60)
.align(Alignment.Start)
Image($r('app.media.ic_settings_arrow'))
.margin({ left: DefaultConstants.DEFAULT_PIXEL_80 })
.height(DefaultConstants.DEFAULT_PIXEL_120)
.width(DefaultConstants.DEFAULT_PIXEL_100)
.align(Alignment.End)
}.onClick(() => {
AppStorage.SetOrCreate('ida', this.ida);
AppStorage.SetOrCreate('valueList', this.valueList);
this.dialogController.open();
}).margin({ top: DefaultConstants.DEFAULT_PIXEL_10,
bottom: DefaultConstants.DEFAULT_PIXEL_10 })
.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.DEFAULT_PIXEL_75)
}
}
}
@CustomDialog
@Component
struct SettingsDialog {
controller: CustomDialogController;
action: () => void;
cancel: () => void;
@StorageLink('valueList') valueList: any = [];
@StorageLink('ida') ida: number = 0;
build() {
Column() {
ForEach(this.valueList, (item: any) => {
Row() {
Text(item.name)
.margin({ left: 10 })
.align(Alignment.Start)
.width(DefaultConstants.PERCENTAGE_85)
.fontSize(DefaultConstants.DEFAULT_PIXEL_30)
.fontColor(DefaultConstants.DEFAULT_DIALOG_FONT_COLOR)
Radio({ value: item.value })
.width(DefaultConstants.DEFAULT_PIXEL_30)
.height(DefaultConstants.DEFAULT_PIXEL_30)
.onChange((isChecked)=>{
mSettingsPresenter.changeSettingValue(this.ida, item.name);
mSettingsPresenter.setSettingsValue(this.ida, item.value);
this.controller.close();
this.action();
})
}.width(DefaultConstants.PERCENTAGE_100)
.height(DefaultConstants.DEFAULT_PIXEL_80)
.onClick(() => {
mSettingsPresenter.changeSettingValue(this.ida, item.name);
mSettingsPresenter.setSettingsValue(this.ida, item.value);
this.controller.close();
this.action();
})
})
Text($r('app.string.cancel'))
.textAlign(TextAlign.Center)
.height(DefaultConstants.DEFAULT_PIXEL_80)
.width(DefaultConstants.PERCENTAGE_100)
.fontSize(DefaultConstants.DEFAULT_PIXEL_30)
.fontColor(Color.Blue)
.onClick(() => {
this.controller.close();
this.action();
})
}.padding(DefaultConstants.DEFAULT_PIXEL_20)
.backgroundColor(DefaultConstants.DEFAULT_SETTING_PAGE_COLOR)
.borderRadius(DefaultConstants.DEFAULT_PIXEL_30)
}
}

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "settings_MainAbility",
"value": "settings_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "layout",
"value": "Layout"
},
{
"name": "intoSettings",
"value": "Launcher settings"
},
{
"name": "layoutStyle",
"value": "Layout Style"
},
{
"name": "launcherLayout",
"value": "Launcher Layout"
},
{
"name": "recentTasksSetting",
"value": "Recent Tasks Setting"
},
{
"name": "cancel",
"value": "Cancel"
}
]
}

View File

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 800 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "settings_MainAbility",
"value": "settings_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "intoSettings",
"value": "Launcher settings"
},
{
"name": "layout",
"value": "Layout"
},
{
"name": "layoutStyle",
"value": "Layout Style"
},
{
"name": "launcherLayout",
"value": "Launcher Layout"
},
{
"name": "recentTasksSetting",
"value": "Recent Tasks Setting"
},
{
"name": "cancel",
"value": "Cancel"
}
]
}

View File

@ -0,0 +1,36 @@
{
"string": [
{
"name": "settings_MainAbility",
"value": "settings_MainAbility"
},
{
"name": "mainability_description",
"value": "ETS_Empty Feature Ability"
},
{
"name": "intoSettings",
"value": "桌面设置"
},
{
"name": "layout",
"value": "布局"
},
{
"name": "layoutStyle",
"value": "布局样式"
},
{
"name": "launcherLayout",
"value": "桌面布局"
},
{
"name": "recentTasksSetting",
"value": "最近任务数"
},
{
"name": "cancel",
"value": "取消"
}
]
}

BIN
figures/cmd1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
figures/cmd2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
figures/cmd3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
figures/ds_build_haps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
figures/ds_code_hint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
figures/ds_download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
figures/ds_exe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
figures/ds_hilog_window.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
figures/ds_i18n_files.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
figures/ds_install_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
figures/ds_install_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
figures/git_clone_done.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Some files were not shown because too many files have changed in this diff Show More