Launcher架构分析
简介 Launcher 作为系统人机交互的首要入口,提供应用图标的显示、点击启动、卸载应用,并提供桌面布局设置以及最近任务管理等功能。 Launcher 采用 扩展的TS语言(eTS)开发,主要的结构如下:product 业务形态层:区分不同产品、不同屏幕的各形态桌面,含有桌面
Launcher 作为系统人机交互的首要入口,提供应用图标的显示、点击启动、卸载应用,并提供桌面布局设置以及最近任务管理等功能。
-
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实现,详解见:
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(); }
更多推荐
所有评论(0)