OpenHarmony开发者论坛

标题: Launcher架构分析 [打印本页]

作者: Laval社区小助手    时间: 2024-3-4 09:26
标题: Launcher架构分析
[md]**简介**

**Launcher 作为系统人机交互的首要入口,提供应用图标的显示、点击启动、卸载应用,并提供桌面布局设置以及最近任务管理等功能。** **Launcher 采用 扩展的TS语言(eTS)开发,主要的结构如下:**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/launcherl2-zh.png?lastModify=1686792052)

![](https://devpress.csdnimg.cn/6f51f5aa3d914eaf864951b0e92e9d3f.png)

* **product** **业务形态层:区分不同产品、不同屏幕的各形态桌面,含有桌面窗口、个性化业务,组件的配置,以及个性化资源包。**
* **feature** **公共特性层:抽象的公共特性组件集合,可以被各桌面形态引用。**
* **common** **公共能力层:基础能力集,每个桌面形态都必须依赖的模块。**

# 代码结构

```
/applications/standard/launcher/
├── common                    # 公共能力层目录
├── docs                      # 开发指南
├── feature                   # 公共特性层目录
│   ├── appcenter             # 应用中心
│   ├── bigfolder             # 智能文件夹
│   ├── form                  # 桌面卡片管理功能
│   ├── gesturenavigation     # 手势导航
│   ├── pagedesktop           # 工作区
│   ├── recents               # 最近任务
│   ├── settings              # 桌面设置
│   └── smartdock             # dock工具栏
├── product                   # 业务形态层目录
└── signature                 # 签名证书
```

# 功能介绍

## 1.应用启动流程

**账户子系统在进行用户切换时,调用foundation\\aafwk\\standard\\services\\abilitymgr\\src\\ability\_manager\_service.cpp中的SwitchToUser**

```
void AbilityManagerService::SwitchToUser(int32_t oldUserId, int32_t userId)
{
    HILOG_INFO("%{public}s, oldUserId:%{public}d, newUserId:%{public}d", __func__, oldUserId, userId);
    SwitchManagers(userId);
    PauseOldUser(oldUserId);
    bool isBoot = false;
    if (oldUserId == U0_USER_ID) {
        isBoot = true;
    }
    StartUserApps(userId, isBoot);
    PauseOldConnectManager(oldUserId);
}
```

**调用StartUserApps拉起用户应用**

```
void AbilityManagerService::StartUserApps(int32_t userId, bool isBoot)
{
    HILOG_INFO("StartUserApps, userId:%{public}d, currentUserId:%{public}d", userId, GetUserId());
#ifdef SUPPORT_GRAPHICS
    if (currentMissionListManager_ && currentMissionListManager_->IsStarted()) {
        HILOG_INFO("missionListManager ResumeManager");
        currentMissionListManager_->ResumeManager();
        return;
    }
#endif
    StartSystemAbilityByUser(userId, isBoot);
}
```

**调用StartSystemAbilityByUser拉起用户系统应用**

```
void AbilityManagerService::StartSystemAbilityByUser(int32_t userId, bool isBoot)
{
    HILOG_INFO("StartSystemAbilityByUser, userId:%{public}d, currentUserId:%{public}d", userId, GetUserId());
    ConnectBmsService();

    if (!amsConfigResolver_ || amsConfigResolver_->NonConfigFile()) {
        HILOG_INFO("start all");
        StartingLauncherAbility(isBoot);
#ifdef SUPPORT_GRAPHICS
        StartingScreenLockAbility();
#endif
        return;
    }

    if (amsConfigResolver_->GetStartLauncherState()) {
        HILOG_INFO("start launcher");
        StartingLauncherAbility(isBoot);
    }

#ifdef SUPPORT_GRAPHICS
    if (amsConfigResolver_->GetStartScreenLockState()) {
        StartingScreenLockAbility();
    }
#endif

    if (amsConfigResolver_->GetPhoneServiceState()) {
        HILOG_INFO("start phone service");
        StartingPhoneServiceAbility();
    }

    if (amsConfigResolver_->GetStartMmsState()) {
        HILOG_INFO("start mms");
        StartingMmsAbility();
    }
}
```

**调用StartingLauncherAbility拉起桌面应用**

**注:这里会等待launcher应用的拉起,会尝试等待SWITCH\_ACCOUNT\_TRY(3)次,每次等待REPOLL\_TIME\_MICRO\_SECONDS(1000000)微秒,也就是1秒**

```
bool AbilityManagerService::StartingLauncherAbility(bool isBoot)
{
    HILOG_DEBUG("%{public}s", __func__);
    auto bms = GetBundleManager();
    CHECK_POINTER_AND_RETURN(bms, false);

    /* query if launcher ability has installed */
    AppExecFwk::AbilityInfo abilityInfo;
    /* First stage, hardcoding for the first launcher App */
    auto userId = GetUserId();
    Want want;
    want.SetElementName(AbilityConfig:AUNCHER_BUNDLE_NAME, AbilityConfig:AUNCHER_ABILITY_NAME);
    HILOG_DEBUG("%{public}s, QueryAbilityInfo, userId is %{public}d", __func__, userId);
    int attemptNums = 0;
    while (!IN_PROCESS_CALL(bms->QueryAbilityInfo(want, AppExecFwk::AbilityInfoFlag::GET_ABILITY_INFO_WITH_APPLICATION,
        userId, abilityInfo))) {
        HILOG_INFO("Waiting query launcher ability info completed.");
        if (!isBoot && ++attemptNums > SWITCH_ACCOUNT_TRY) {
            HILOG_ERROR("Start launcher failed.");
            return false;
        }
        usleep(REPOLL_TIME_MICRO_SECONDS);
    }

    HILOG_INFO("Start Home Launcher Ability.");
    /* start launch ability */
    (void)StartAbility(want, userId, DEFAULT_INVAL_VALUE);
    return true;
}
```

**StartAbility底层源码就不再追溯,通过want,拉起应用。**

## 2.主体功能介绍

**注:这里以3568为例**

### 2.1应用中心

#### 2.1.1应用管理

**注:应用长按,弹出【打开】、【卸载】操作弹窗**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/%E5%BA%94%E7%94%A8%E7%AE%A1%E7%90%86.jpg?lastModify=1686792052)

![](https://devpress.csdnimg.cn/2c7c4b35ccf84e27970ae4a7057f4198.jpg)

**点击【打开】,拉起应用;**

**点击【卸载】,卸载应用。**

#### 2.1.2桌面管理

**注:桌面空白区域长按,弹出【桌面设置】、【添加空白页】操作弹窗**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/%E6%A1%8C%E9%9D%A2%E7%AE%A1%E7%90%86.jpg?lastModify=1686792052)

![](https://devpress.csdnimg.cn/fc1ae16b5c6f42b3a884006eb886b564.jpg)

**点击【桌面设置】,弹出手势导航开关设置页面**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/%E6%A1%8C%E9%9D%A2%E8%AE%BE%E7%BD%AE.jpg?lastModify=1686792052)

![](https://devpress.csdnimg.cn/c5fa600de93a44a9add1666b8c326cb6.jpg)

**手势开启后,桌面导航栏按键隐藏**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/%E5%BC%80%E5%90%AF%E6%89%8B%E5%8A%BF.jpg?lastModify=1686792052)

![](https://devpress.csdnimg.cn/52b5f7eff5584e40a73d5a60aef490c0.jpg)

**手势开启后,可通过短按左划或右划进行返回操作,短按上划返回桌面,长按上划进入后台任务窗口(所有的手势操作都要从对应的屏幕边缘开始)**

**注:具体功能见手势管理**

#### 2.1.3桌面背景

**桌面默认背景图:applications\_launcher\\feature\\appcenter\\src\\main\\ets\\default\\common\\pics\\img\_wallpaper\_default.jpg**

### 2.2文件夹管理

**当应用拖拽区域重叠时,自动创建文件夹,用于放置应用。其中包含:文件夹重命名、移出文件夹、添加新应用等功能。**

![](file:///C:/Users/kuangansheng/Desktop/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B+%E6%A1%88%E4%BE%8B/%E8%AF%BE%E7%A8%8B/Launcher%E6%9E%B6%E6%9E%84%E5%88%86%E6%9E%90/image/%E6%96%87%E4%BB%B6%E5%A4%B9%E7%AE%A1%E7%90%86.jpg?lastModify=1686792052)

![](https://devpress.csdnimg.cn/df2e6671bb2c4803921707e2499c38cc.jpg)

![](https://devpress.csdnimg.cn/c8c9a5024b2645ffa015deab68636f10.jpg)

#### 2.2.1移出文件夹

```
/**
 * Delete app from open folder
 *
 * @param {any} appInfo.
 */
deleteAppFromOpenFolder(appInfo): any {
  let openFolderData: {
    folderId: string,
    layoutInfo: any
  } = AppStorage.Get('openFolderData');
  const folderLayoutInfo = this.getFolderLayoutInfo(openFolderData, appInfo);

  // Delete app from the folder
  const gridLayoutInfo = this.mSettingsModel.getLayoutInfo();
  const folderIndex = gridLayoutInfo.layoutInfo.findIndex(item => {
    return item.typeId === CommonConstants.TYPE_FOLDER && item.folderId === openFolderData.folderId;
  });

  const appListInfo = this.mSettingsModel.getAppListInfo();
  if (folderLayoutInfo.length == 1 && folderLayoutInfo[0].length == 1) {
    // delete from folder and add app to desktop
    const appLayout = {
      bundleName: folderLayoutInfo[0][0].bundleName,
      abilityName: folderLayoutInfo[0][0].abilityName,
      moduleName: folderLayoutInfo[0][0].moduleName,
      keyName: folderLayoutInfo[0][0].keyName,
      typeId: folderLayoutInfo[0][0].typeId,
      area: folderLayoutInfo[0][0].area,
      page: gridLayoutInfo.layoutInfo[folderIndex].page,
      column: gridLayoutInfo.layoutInfo[folderIndex].column,
      row: gridLayoutInfo.layoutInfo[folderIndex].row
    };
    gridLayoutInfo.layoutInfo.push(appLayout);
    appListInfo.push(folderLayoutInfo[0][0]);
    gridLayoutInfo.layoutInfo.splice(folderIndex, 1);
    openFolderData = {
      folderId: '', layoutInfo: []
    };
  } else {
    this.updateBadgeNumber(gridLayoutInfo.layoutInfo[folderIndex], appInfo);
    openFolderData.layoutInfo = folderLayoutInfo;
  }
  this.mSettingsModel.setAppListInfo(appListInfo);
  this.mSettingsModel.setLayoutInfo(gridLayoutInfo);
  return openFolderData;
}
```

**查看应用源码,其实就是从layout中移除指定应用信息(注:当文件夹中只剩一个应用时,自动移出文件夹,文件夹布局信息移除),然后更新布局**

**这里我们关注下应用信息和布局信息的存储:**

```
async insertDesktopApplication(desktopApplicationInfo: any): Promise<boolean> {
  Log.showInfo(TAG, 'insertDesktopApplication start');
  let result: boolean = true;
  if (CheckEmptyUtils.isEmptyArr(desktopApplicationInfo)) {
    Log.showError(TAG, 'insertDesktopApplication desktopApplicationInfo is empty');
    result = false;
    return result;
  }
  try {
    this.mRdbStore.beginTransaction();
    // delete desktopApplicationInfo table
    await this.deleteTable(RdbStoreConfig.DesktopApplicationInfo.TABLE_NAME);
    // insert into desktopApplicationInfo
    for (let i in desktopApplicationInfo) {
      let element = desktopApplicationInfo;
      let item = {
        'app_name': element.appName,
        'is_system_app': element.isSystemApp ? 1 : 0,
        'is_uninstallAble': element.isUninstallAble ? 1 : 0,
        'appIcon_id': element.appIconId,
        'appLabel_id': element.appLabelId,
        'bundle_name': element.bundleName,
        'module_name': element.moduleName,
        'ability_name': element.abilityName,
        'key_name': element.bundleName + element.abilityName + element.moduleName,
        'install_time': element.installTime
      }
      this.mRdbStore.insert(RdbStoreConfig.DesktopApplicationInfo.TABLE_NAME, item)
        .then((ret) => {
          Log.showDebug(TAG, `insertDesktopApplication ${i} ret: ${ret}`);
          if (ret === -1) {
            result = false;
          }
        });
    }
    this.mRdbStore.commit();
  } catch (e) {
    Log.showError(TAG, 'insertDesktopApplication error:' + e);
    this.mRdbStore.rollBack();
  }
  return result;
}
```

**应用信息是通过rdb进行持久化的,循环存储在Launcher.db的RdbStoreConfig.DesktopApplicationInfo.TABLE\_NAME(DESKTOPAPPLICATIONINFO)数据表中,设备存储库路径为:/data/app/el2/100/database/com.ohos.launcher/phone-launcher/db/Launcher.db**

```
/**
 * Update workspace layout data.
 *
 * @params gridLayoutInfo
 */
updateGridLayoutInfo(gridLayoutInfo: any): void {
  const temp = {
    layoutDescription: {},
    layoutInfo: []
  };
  temp.layoutDescription = gridLayoutInfo.layoutDescription;
  FileUtils.writeStringToFile(JSON.stringify(temp), this.getConfigFileAbsPath());
  this.mGridLayoutInfo = gridLayoutInfo;
  globalThis.RdbStoreManagerInstance.insertGridLayoutInfo(gridLayoutInfo).then(() => {
    Log.showInfo(TAG, 'updateGridLayoutInfo success.');
  }).catch((err) => {
    Log.showError(TAG, `updateGridLayoutInfo error: ${err.toString()}`);
  });
}
```

**更新布局信息时,会先将布局描述写入到文件中,再将布局信息持久化到存储库**

```
/**
* Write string to a file.
*
* @param {string} str - target string will be written to file.
* @param {string} filePath - filePath as the absolute path to the target file.
*/
static writeStringToFile(str: string, filePath: string): void {
  Log.showDebug(TAG, 'writeStringToFile start execution');
  let writeStreamSync = null;
  try {
    writeStreamSync = Fileio.createStreamSync(filePath, 'w+');
    let number = writeStreamSync.writeSync(str);
    Log.showInfo(TAG, 'writeStringToFile number: ' + number);
  } catch (e) {
    Log.showError(TAG, `writeStringToFile error: ${e.toString()}`);
  } finally {
    writeStreamSync.closeSync();
    Log.showDebug(TAG, 'writeStringToFile close sync');
  }
}
```

**布局描述写入到文件(/data/app/el2/100/base/com.ohos.launcher/haps/phone-launcher/files/GridLayoutInfo.json)中**

**示例内容:**

```
{"layoutDescription":{"pageCount":1,"row":6,"column":5},"layoutInfo":[]}
```

```
async insertGridLayoutInfo(gridlayoutinfo: any): Promise<void> {
  Log.showInfo(TAG, 'insertGridLayoutInfo start');
  if (CheckEmptyUtils.isEmpty(gridlayoutinfo) || CheckEmptyUtils.isEmptyArr(gridlayoutinfo.layoutInfo)) {
    Log.showError(TAG, 'insertGridLayoutInfo gridlayoutinfo is empty');
    return;
  }

  try {
    this.mRdbStore.beginTransaction();
    // delete gridlayoutinfo table
    await this.dropTable(RdbStoreConfig.GridLayoutInfo.TABLE_NAME);
    // insert into gridlayoutinfo
    let layoutinfo: any[] = gridlayoutinfo.layoutInfo;
    for (let i in layoutinfo) {
      let element = layoutinfo;
      let item = {};
      Log.showDebug(TAG, 'insertGridLayoutInfo' + JSON.stringify(element));
      if (element.typeId === CommonConstants.TYPE_APP) {
        item = {
          'bundle_name': element.bundleName,
          'ability_name': element.abilityName,
          'module_name': element.moduleName,
          'key_name': element.bundleName + element.abilityName + element.moduleName,
          'type_id': element.typeId,
          'area': element.area[0] + ',' + element.area[1],
          'page': element.page,
          'column': element.column,
          'row': element.row,
          'container': -100
        }
        this.mRdbStore.insert(RdbStoreConfig.GridLayoutInfo.TABLE_NAME, item)
          .then((ret) => {
            Log.showDebug(TAG, `insertGridLayoutInfo type is app ${i} ret: ${ret}`);
          });
      } else if (element.typeId === CommonConstants.TYPE_CARD) {
        item = {
          'bundle_name':element.bundleName,
          'ability_name': element.abilityName,
          'module_name': element.moduleName,
          'key_name': "" + element.cardId,
          'card_id': element.cardId,
          'type_id': element.typeId,
          'area': element.area[0] + ',' + element.area[1],
          'page': element.page,
          'column': element.column,
          'row': element.row,
          'container': -100
        }
        this.mRdbStore.insert(RdbStoreConfig.GridLayoutInfo.TABLE_NAME, item)
          .then((ret) => {
            Log.showDebug(TAG, `insertGridLayoutInfo type is card ${i} ret: ${ret}`);
          });
      } else {
        item = {
          'bundle_name':element.bundleName,
          'ability_name': element.abilityName,
          'module_name': element.moduleName,
          'folder_id': element.folderId,
          'folder_name': element.folderName,
          'type_id': element.typeId,
          'area': element.area[0] + ',' + element.area[1],
          'page': element.page,
          'column': element.column,
          'row': element.row,
          'container': -100,
          'badge_number': element.badgeNumber
        }
        this.mRdbStore.insert(RdbStoreConfig.GridLayoutInfo.TABLE_NAME, item).then(ret => {
          if (ret != -1) {
            this.insertLayoutInfo(element.layoutInfo, ret);
          }
          Log.showDebug(TAG, `insertGridLayoutInfo type is bigfolder ${i} ret: ${ret}`);
        });
      }
    }
    this.mRdbStore.commit();
  } catch (e) {
    Log.showError(TAG, 'insertGridLayoutInfo error:' + e);
    this.mRdbStore.rollBack();
  }
}
```

**布局信息循环持久化在RdbStoreConfig.GridLayoutInfo.TABLE\_NAME(GRIDLAYOUTINFO)数据表中,数据类型分为:app、card和bigfolder,其中会存储各个类型的应用信息、特征信息及布局信息。**

#### 2.2.2添加新应用

**文件夹相关的操作其实都是针对布局信息的调整更新,基本都是类似逻辑。**

### 2.3卡片管理

**该功能设备上暂未开放,暂不做分析了。**

### 2.4手势管理

**当桌面设置手势导航开关打开后,手势生效**

```
initWindowSize(display: any) {
  if (globalThis.sGestureNavigationExecutors) {
    globalThis.sGestureNavigationExecutors.setScreenWidth(display.width);
    globalThis.sGestureNavigationExecutors.setScreenHeight(display.height);
    this.touchEventCallback = globalThis.sGestureNavigationExecutors.touchEventCallback
      .bind(globalThis.sGestureNavigationExecutors);
    this.getGestureNavigationStatus();
  }
}
```

**手势生效与关闭都会进行窗口尺寸设置,隐藏或显示导航栏**

```
/**
* touchEvent Callback.
* @return true: Returns true if the gesture is within the specified hot zone.
*/
touchEventCallback(event: any): boolean {
  Log.showDebug(TAG, 'touchEventCallback enter');
  if (event.touches.length != 1) {
    return false;
  }
  const startXPosition = event.touches[0].globalX;
  const startYPosition = event.touches[0].globalY;
  if (event.type == 'down' && this.isSpecifiesRegion(startXPosition, startYPosition)) {
    this.initializationParameters();
    this.startEventPosition = this.preEventPosition = {
      x: startXPosition,
      y: startYPosition
    };
    this.startTime = this.preEventTime = event.timestamp;
    this.curEventType = event.type;
    if (vp2px(16) >= startXPosition || startXPosition >= (this.screenWidth - vp2px(16))) {
      this.eventName = 'backEvent';
      return true;
    }
  }
  if (this.startEventPosition && this.isSpecifiesRegion(this.startEventPosition.x, this.startEventPosition.y)) {
    if (event.type == 'move') {
      this.curEventType = event.type;
      const curTime = event.timestamp;
      const speedX = (startXPosition - this.preEventPosition.x) / ((curTime - this.preEventTime) / 1000);
      const speedY = (startYPosition - this.preEventPosition.y) / ((curTime - this.preEventTime) / 1000);
      const sqrt = Math.sqrt(speedX * speedX + speedY * speedY);
      const curSpeed = startYPosition <= this.preEventPosition.y ? -sqrt : sqrt;
      const acceleration = (curSpeed - this.preSpeed) / ((curTime - this.preEventTime) / 1000);
      this.preEventPosition = {
        x: startXPosition,
        y: startYPosition
      };
      this.preSpeed = curSpeed;
      const isDistance = this.isRecentsViewShowOfDistanceLimit(startYPosition);
      const isSpeed = this.isRecentsViewShowOfSpeedLimit(curTime, acceleration, curSpeed);
      this.preEventTime = curTime;
      if (isDistance && isSpeed && !this.eventName && curSpeed) {
        this.eventName = 'recentEvent';
        this.recentEventCall();
        return true;
      }
      if (this.eventName == 'backEvent' && startXPosition > vp2px(16) && !this.timeOfFirstLeavingTheBackEventHotArea) {
        this.timeOfFirstLeavingTheBackEventHotArea = (curTime - this.startTime) / 1000;
      }
    }
    if (event.type == 'up') {
      let distance = 0;
      let slidingSpeed = 0;
      if (this.curEventType == 'move') {
        if (this.eventName == 'backEvent') {
          distance = Math.abs((startXPosition - this.startEventPosition.x));
          if (distance >= vp2px(16) * 1.2 && this.timeOfFirstLeavingTheBackEventHotArea <= 120) {
            this.backEventCall();
            this.initializationParameters();
            return true;
          }
        } else if (this.eventName == 'recentEvent') {
          this.initializationParameters();
          return true;
        } else {
          distance = this.startEventPosition.y - startYPosition;
          const isDistance = this.isHomeViewShowOfDistanceLimit(startYPosition);
          Log.showDebug(TAG, `touchEventCallback isDistance: ${isDistance}`);
          if (isDistance) {
            slidingSpeed = distance / ((event.timestamp - this.startTime) / GestureNavigationExecutors.NS_PER_MS);
            Log.showDebug(TAG, `touchEventCallback homeEvent slidingSpeed: ${slidingSpeed}`);
            if (slidingSpeed >= vp2px(500)) {
              this.homeEventCall();
            }
            this.initializationParameters();
            return true;
          }
        }
      }
      this.initializationParameters();
    }
  }
  return false;
}
```

**从touchEventCallback中可以看出,手势分为:向下、向上和移动三种,执行手势操作时,会记录手势的起始坐标和开始时间,手势执行结束后,根据结束位置和结束时间,来计算距离和速度,从而来执行相应的操作,例:返回操作(this.backEventCall())、HOME操作(this.homeEventCall())及RECENT操作(this.recentEventCall())。**

**注:手势操作需要从特殊区域开始,判断逻辑如下:**

```
private isSpecifiesRegion(startXPosition: number, startYPosition: number) {
  const isStatusBarRegion = startYPosition <= this.screenHeight * 0.07;
  const isSpecifiesXRegion = startXPosition <= vp2px(16) || startXPosition >= (this.screenWidth - vp2px(16));
  const isSpecifiesYRegion = (this.screenHeight - vp2px(22)) <= startYPosition && startYPosition <= this.screenHeight;
  return (isSpecifiesXRegion && !isStatusBarRegion) || (isSpecifiesYRegion && !isSpecifiesXRegion);
}
```

### 2.5工作区

**工作区即设备桌面,这里主要针对布局信息进行桌面渲染,然后针对拖拽事件的处理。**

```
onDragDrop(x: number, y: number): boolean {
  const dragItemInfo: any = AppStorage.Get('dragItemInfo');
  if (JSON.stringify(dragItemInfo) == '{}') {
    return false;
  }
  const dragItemType: number = AppStorage.Get('dragItemType');
  const deviceType: string = AppStorage.Get('deviceType')
  // dock appInfo has no location information.
  if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
    dragItemInfo.typeId = CommonConstants.TYPE_APP;
    dragItemInfo.area = [1, 1];
    dragItemInfo.page = AppStorage.Get('pageIndex');
  }
  Log.showDebug(TAG, `onDragEnd dragItemInfo: ${JSON.stringify(dragItemInfo)}`);
  const endIndex = this.getItemIndex(x, y);
  const startPosition: DragItemPosition = this.copyPosition(this.mStartPosition);
  let endPosition: DragItemPosition = null;
  this.mEndPosition = this.getTouchPosition(x, y);
  Log.showInfo(TAG, `onDragEnd mEndPosition: ${JSON.stringify(this.mEndPosition)}`);
  endPosition = this.copyPosition(this.mEndPosition);
  const info = this.mSettingsModel.getLayoutInfo();
  const layoutInfo = info.layoutInfo;
  if (dragItemInfo.typeId == CommonConstants.TYPE_FOLDER || dragItemInfo.typeId == CommonConstants.TYPE_CARD ) {
    this.updateEndPosition(dragItemInfo);
    AppStorage.SetOrCreate('positionOffset', []);
  } else {
    if (this.isMoveToSamePosition(dragItemInfo)) {
      this.deleteBlankPageAfterDragging(startPosition, endPosition);
      return false;
    }
    const endLayoutInfo = this.getEndLayoutInfo(layoutInfo);
    if (endLayoutInfo != undefined) {
      // add app to folder
      if (endLayoutInfo.typeId === CommonConstants.TYPE_FOLDER) {
        this.mBigFolderViewModel.addOneAppToFolder(dragItemInfo, endLayoutInfo.folderId);
        if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
          localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
        }
        this.deleteBlankPageAfterDragging(startPosition, endPosition);
        return true;
      } else if (endLayoutInfo.typeId === CommonConstants.TYPE_APP) {
        // create a new folder
        const layoutInfoList = [endLayoutInfo];
        let startLayoutInfo = null;
        if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
          let appInfoList = this.mSettingsModel.getAppListInfo();
          const appIndex = appInfoList.findIndex(item => {
            return item.keyName === dragItemInfo.keyName;
          })
          if (appIndex == CommonConstants.INVALID_VALUE) {
            appInfoList.push({
              "appName": dragItemInfo.appName,
              "isSystemApp": dragItemInfo.isSystemApp,
              "isUninstallAble": dragItemInfo.isUninstallAble,
              "appIconId": dragItemInfo.appIconId,
              "appLabelId": dragItemInfo.appLabelId,
              "bundleName": dragItemInfo.bundleName,
              "abilityName": dragItemInfo.abilityName,
              "moduleName": dragItemInfo.moduleName,
              "keyName": dragItemInfo.keyName,
              "typeId": dragItemInfo.typeId,
              "area": dragItemInfo.area,
              "page": dragItemInfo.page,
              "column": this.getColumn(endIndex),
              "row": this.getRow(endIndex),
              "x": 0,
              "installTime": dragItemInfo.installTime
            })
            this.mSettingsModel.setAppListInfo(appInfoList);
          }
          startLayoutInfo = dragItemInfo;
          localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
        } else {
          startLayoutInfo = this.getStartLayoutInfo(layoutInfo, dragItemInfo);
        }
        layoutInfoList.push(startLayoutInfo);
        this.mBigFolderViewModel.addNewFolder(layoutInfoList).then(()=> {
          this.deleteBlankPageAfterDragging(startPosition, endPosition);
        });
        return true;
      }
    }
  }

  if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
    let appInfoTemp = {
      "bundleName": dragItemInfo.bundleName,
      "typeId": dragItemInfo.typeId,
      "abilityName": dragItemInfo.abilityName,
      "moduleName": dragItemInfo.moduleName,
      "keyName": dragItemInfo.keyName,
      "area": dragItemInfo.area,
      "page": dragItemInfo.page,
      "column": this.getColumn(endIndex),
      "row": this.getRow(endIndex)
    };
    layoutInfo.push(appInfoTemp);
    localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
  } else {
    this.checkAndMove(this.mStartPosition, this.mEndPosition, layoutInfo, dragItemInfo);
  }

  info.layoutInfo = layoutInfo;
  this.mSettingsModel.setLayoutInfo(info);
  localEventManager.sendLocalEventSticky(EventConstants.EVENT_SMARTDOCK_INIT_FINISHED, null);
  this.deleteBlankPageAfterDragging(startPosition, endPosition);
  return true;
}
```

**其中就有拖拽应用创建文件夹(this.mBigFolderViewModel.addNewFolder(layoutInfoList)),拖拽应用到文件夹(this.mBigFolderViewModel.addOneAppToFolder(dragItemInfo, endLayoutInfo.folderId)),拖拽应用、文件夹、卡片布局位置等操作。**

### 2.6最近任务

**最近任务即Recent窗口,主要是对后台任务的管理,主体功能通过MessionManager实现,详解见:**

[OpenHarmony任务管理MissionManager](https://codehub-g.huawei.com/ope ... amp;login=from_csdn)

### 2.7桌面设置

**桌面设置主要涉及添加空白页和手势导航开关设置功能,具体功能不多做介绍了。**

## 3.桌面初始化

**Launcher初始化流程如下:初始化上下文、初始化全局常量、初始化手势导航、初始化rdb、注册窗口事件、注册导航栏事件、创建桌面窗口(加载pages/EntryView)、创建Recent窗口**

```
async initLauncher(): Promise<void> {
  // init Launcher context
  globalThis.desktopContext = this.context;

  // init global const
  this.initGlobalConst();

  // init Gesture navigation
  this.startGestureNavigation();

  // init rdb
  let dbStore = RdbStoreManager.getInstance();
  await dbStore.initRdbConfig();
  await dbStore.createTable();

  windowManager.registerWindowEvent();
  navigationBarCommonEventManager.registerNavigationBarEvent();

  // create Launcher entry view
  windowManager.createWindow(globalThis.desktopContext, windowManager.DESKTOP_WINDOW_NAME,
    windowManager.DESKTOP_RANK, 'pages/' + windowManager.DESKTOP_WINDOW_NAME);

  // load recent
  windowManager.createRecentWindow();
}
```

**桌面渲染即EntryView的页面渲染,通过AppInfo进行页面布局。**
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5