简介

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

 

  • 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::LAUNCHER_BUNDLE_NAME, AbilityConfig::LAUNCHER_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应用管理

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

 

 

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

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

2.1.2桌面管理

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

 

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

 

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

 

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

注:具体功能见手势管理

2.1.3桌面背景

桌面默认背景图:applications_launcher\feature\appcenter\src\main\ets\default\common\pics\img_wallpaper_default.jpg

 

2.2文件夹管理

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

 

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[i];       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[i]; 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

 

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进行页面布局。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐