1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { LauncherDragItemInfo, Log, RecentBundleMissionInfo, ResourceManager } from '@ohos/common'; 17import { Trace } from '@ohos/common'; 18import { CheckEmptyUtils } from '@ohos/common'; 19import { MenuInfo } from '@ohos/common'; 20import { MissionInfo } from '@ohos/common'; 21import { DockItemInfo } from '@ohos/common'; 22import { windowManager } from '@ohos/common'; 23import { layoutConfigManager } from '@ohos/common'; 24import { amsMissionManager }from '@ohos/common'; 25import { launcherAbilityManager } from '@ohos/common'; 26import { CommonConstants } from '@ohos/common'; 27import { BaseViewModel } from '@ohos/common'; 28import SmartDockStartAppHandler from '../common/SmartDockStartAppHandler'; 29import SmartDockModel from '../model/SmartDockModel'; 30import SmartDockDragHandler from '../common/SmartDockDragHandler'; 31import { SmartDockStyleConfig } from '../config/SmartDockStyleConfig'; 32import SmartDockConstants from '../common/constants/SmartDockConstants'; 33import { SmartDockLayoutConfig } from '../config/SmartDockLayoutConfig'; 34 35const TAG = 'SmartDockViewModel'; 36 37class StartAppItemInfo extends LauncherDragItemInfo { 38 icon?: ResourceStr; 39} 40 41/** 42 * SmartDock Viewmodel 43 */ 44export default class SmartDockViewModel extends BaseViewModel { 45 private readonly mSmartDockLayoutConfig: SmartDockLayoutConfig; 46 private readonly mSmartDockStyleConfig: SmartDockStyleConfig; 47 private readonly mSmartDockDragHandler: SmartDockDragHandler; 48 private readonly mSmartDockStartAppHandler: SmartDockStartAppHandler; 49 private readonly mSmartDockModel: SmartDockModel; 50 private mSelectedItem: DockItemInfo | null = null; 51 private mSelectedDockType = 0; 52 private mDevice = CommonConstants.DEFAULT_DEVICE_TYPE; 53 54 constructor() { 55 super(); 56 this.mSmartDockLayoutConfig = layoutConfigManager.getFunctionConfig(SmartDockLayoutConfig.SMART_DOCK_LAYOUT_INFO); 57 this.mSmartDockStyleConfig = layoutConfigManager.getStyleConfig(SmartDockStyleConfig.APP_LIST_STYLE_CONFIG, SmartDockConstants.FEATURE_NAME); 58 this.mSmartDockDragHandler = SmartDockDragHandler.getInstance(); 59 this.mSmartDockStartAppHandler = SmartDockStartAppHandler.getInstance(); 60 this.mSmartDockModel = SmartDockModel.getInstance(); 61 } 62 63 static getInstance(): SmartDockViewModel{ 64 if (globalThis.SmartDockViewModel == null) { 65 globalThis.SmartDockViewModel = new SmartDockViewModel(); 66 } 67 return globalThis.SmartDockViewModel; 68 } 69 70 /** 71 * get SmartDockStyleConfig 72 */ 73 getStyleConfig(): SmartDockStyleConfig{ 74 return layoutConfigManager.getStyleConfig(SmartDockStyleConfig.APP_LIST_STYLE_CONFIG, SmartDockConstants.FEATURE_NAME); 75 } 76 77 /** 78 * resident dock item onClick function 79 * @param event 80 * @param item 81 * @param callback 82 */ 83 residentOnClick(event: ClickEvent | null, item: DockItemInfo, callback?: () => void) { 84 // AppCenter entry 85 AppStorage.setOrCreate('startAppTypeFromPageDesktop', CommonConstants.OVERLAY_TYPE_APP_RESIDENTIAL); 86 if (item.abilityName == CommonConstants.APPCENTER_ABILITY && callback != null) { 87 callback(); 88 return; 89 } 90 if (item.abilityName == CommonConstants.RECENT_ABILITY) { 91 windowManager.createWindowWithName(windowManager.RECENT_WINDOW_NAME, windowManager.RECENT_RANK); 92 Trace.start(Trace.CORE_METHOD_START_RECENTS); 93 return; 94 } 95 // app entry 96 Trace.start(Trace.CORE_METHOD_START_APP_ANIMATION); 97 this.setStartAppInfo(item as StartAppItemInfo); 98 launcherAbilityManager.startLauncherAbility(item.abilityName, item.bundleName, item.moduleName); 99 } 100 101 /** 102 * recent dock item onClick function 103 * @param event 104 * @param item 105 */ 106 public recentOnClick(event: ClickEvent | null, item: DockItemInfo, callback?: () => void) { 107 AppStorage.setOrCreate('startAppTypeFromPageDesktop', CommonConstants.OVERLAY_TYPE_APP_RECENT); 108 let missionInfoList: RecentBundleMissionInfo[] = []; 109 missionInfoList = AppStorage.get('missionInfoList'); 110 Log.showDebug(TAG, `recentOnClick missionInfoList.length: ${missionInfoList.length}`); 111 if (!CheckEmptyUtils.isEmptyArr(missionInfoList)) { 112 for (let i = 0; i < missionInfoList.length; i++) { 113 if (missionInfoList[i].bundleName === item.bundleName) { 114 let missionList = missionInfoList[i]?.missionInfoList; 115 Log.showDebug(TAG, 'recentOnClick missionList.length:' + missionList.length); 116 if (!CheckEmptyUtils.isEmptyArr(missionList) && missionList.length > 1) { 117 Log.showDebug(TAG, 'recentOnClick callback'); 118 callback(); 119 } else if (!CheckEmptyUtils.isEmptyArr(missionList) && missionList.length === 1) { 120 let missionId = missionInfoList[i]?.missionInfoList[0]?.missionId; 121 amsMissionManager.moveMissionToFront(missionId).then(() => {}, () => {}); 122 // set start app info 123 Trace.start(Trace.CORE_METHOD_START_APP_ANIMATION); 124 this.setStartAppInfo(item as StartAppItemInfo); 125 } 126 break; 127 } 128 } 129 } 130 } 131 132 /** 133 * what SmartDockContent.dockItemList onChange 134 */ 135 onDockListChange() { 136 this.updateDockParams().then(() => {}, () => {}); 137 } 138 139 /** 140 * update drag effective area when dockList changed 141 */ 142 async updateDockParams() { 143 const screenWidth: number = AppStorage.get('screenWidth') as number; 144 const screenHeight: number = AppStorage.get('screenHeight') as number; 145 const sysUIBottomHeight: number = AppStorage.get('sysUIBottomHeight') as number; 146 const dockHeight: number = AppStorage.get('dockHeight') as number; 147 let mResidentWidth: number = this.getListWidth(AppStorage.get('residentList')); 148 if ((AppStorage.get('deviceType') as string) === CommonConstants.DEFAULT_DEVICE_TYPE) { 149 const maxDockNum = this.getStyleConfig().mMaxDockNum; 150 mResidentWidth = this.mSmartDockStyleConfig.mDockPadding * 2 + 151 maxDockNum * (this.mSmartDockStyleConfig.mListItemWidth) + 152 (maxDockNum - 1) * (this.mSmartDockStyleConfig.mListItemGap); 153 } 154 AppStorage.setOrCreate('residentWidth', mResidentWidth); 155 AppStorage.setOrCreate("dockPadding", this.getDockPadding(mResidentWidth)); 156 const mRecentWidth: number = this.getListWidth(AppStorage.get('recentList')); 157 Log.showDebug(TAG, `updateDockParams screenWidth:${screenWidth}, screenHeight:${screenHeight}, sysUIBottomHeight:${sysUIBottomHeight}, dockHeight:${dockHeight}, mResidentWidth:${mResidentWidth}, mRecentWidth:${mRecentWidth}`); 158 if (typeof (this.mSmartDockDragHandler) != 'undefined') { 159 let left = mResidentWidth === 0 ? 0 : (screenWidth - mResidentWidth - (mRecentWidth === 0 ? 0 : (this.mSmartDockStyleConfig.mDockGap + mRecentWidth))) / 2; 160 let right = mResidentWidth === 0 ? screenWidth : (screenWidth - mResidentWidth - (mRecentWidth === 0 ? 0 : (this.mSmartDockStyleConfig.mDockGap + mRecentWidth))) / 2 + mResidentWidth; 161 if ((AppStorage.get('deviceType') as string) == CommonConstants.DEFAULT_DEVICE_TYPE) { 162 left = (screenWidth - mResidentWidth) / 2; 163 right = screenWidth - left; 164 } 165 this.mSmartDockDragHandler.setDragEffectArea({ 166 left: left, 167 right: right, 168 top: screenHeight - sysUIBottomHeight - dockHeight, 169 bottom: screenHeight - sysUIBottomHeight - this.mSmartDockStyleConfig.mMarginBottom 170 }); 171 } 172 } 173 174 private getDockPadding(residentWidth: number): {right: number, left: number, top: number, bottom: number} { 175 let paddingNum: number = this.mSmartDockStyleConfig.mDockPadding; 176 const residentList: [] = AppStorage.get('residentList'); 177 if (AppStorage.get("deviceType") === CommonConstants.DEFAULT_DEVICE_TYPE) { 178 paddingNum = (residentWidth - (residentList.length * this.mSmartDockStyleConfig.mListItemWidth + (residentList.length - 1) * (this.mSmartDockStyleConfig.mListItemGap))) / 2; 179 } 180 Log.showDebug(TAG, `getDockPadding paddingNum: ${paddingNum}`); 181 return {right: paddingNum, left: paddingNum, top: this.mSmartDockStyleConfig.mDockPadding, bottom: this.mSmartDockStyleConfig.mDockPadding}; 182 } 183 184 /** 185 * build menu for @Component CustomOverlay 186 * @param appInfo 187 * @param dockType 188 * @param callback 189 */ 190 buildMenuInfoList(appInfo: DockItemInfo, dockType: number, showAppcenter: () => void, callback?: () => void) { 191 const menuInfoList = new Array<MenuInfo>(); 192 const shortcutInfo = this.mSmartDockModel.getShortcutInfo(appInfo.bundleName); 193 if (shortcutInfo) { 194 let menu: MenuInfo | null = null; 195 shortcutInfo.forEach((value) => { 196 menu = new MenuInfo(); 197 menu.menuType = CommonConstants.MENU_TYPE_DYNAMIC; 198 menu.menuImgSrc = value.icon; 199 menu.menuText = value.label; 200 menu.shortcutIconId = value.iconId; 201 menu.shortcutLabelId = value.labelId; 202 menu.bundleName = value.bundleName; 203 menu.moduleName = value.moduleName; 204 menu.onMenuClick = () => { 205 Trace.start(Trace.CORE_METHOD_START_APP_ANIMATION); 206 launcherAbilityManager.startLauncherAbility(value.wants[0].targetAbility, 207 value.wants[0].targetBundle, value.wants[0].targetModule); 208 }; 209 value.bundleName == appInfo.bundleName && value.moduleName == appInfo.moduleName && menuInfoList.push(menu); 210 }); 211 } 212 213 let open = new MenuInfo(); 214 open.menuType = CommonConstants.MENU_TYPE_FIXED; 215 open.menuImgSrc = '/common/pics/ic_public_add_norm.svg'; 216 open.menuText = $r('app.string.app_menu_open'); 217 open.onMenuClick = () => { 218 this.residentOnClick(null, appInfo, showAppcenter); 219 }; 220 menuInfoList.push(open); 221 222 if (appInfo.itemType != CommonConstants.TYPE_FUNCTION) { 223 this.mDevice = AppStorage.get('deviceType') as string; 224 if (this.mDevice === CommonConstants.PAD_DEVICE_TYPE && dockType === SmartDockConstants.RESIDENT_DOCK_TYPE) { 225 const addToWorkSpaceMenu = new MenuInfo(); 226 addToWorkSpaceMenu.menuType = CommonConstants.MENU_TYPE_FIXED; 227 addToWorkSpaceMenu.menuImgSrc = '/common/pics/ic_public_copy.svg'; 228 addToWorkSpaceMenu.menuText = $r('app.string.app_center_menu_add_desktop'); 229 addToWorkSpaceMenu.onMenuClick = () => { 230 Log.showDebug(TAG, 'onMenuClick item add to pageDesk:' + appInfo.bundleName); 231 this.mSmartDockModel.addToPageDesk(appInfo); 232 }; 233 menuInfoList.push(addToWorkSpaceMenu); 234 } 235 236 const removeMenu = new MenuInfo(); 237 removeMenu.menuType = CommonConstants.MENU_TYPE_FIXED; 238 removeMenu.menuImgSrc = this.mDevice === CommonConstants.PAD_DEVICE_TYPE ? '/common/pics/ic_public_remove.svg' : '/common/pics/ic_public_delete.svg'; 239 removeMenu.menuText = this.mDevice === CommonConstants.PAD_DEVICE_TYPE ? $r('app.string.delete_app') : $r('app.string.uninstall'); 240 removeMenu.onMenuClick = () => { 241 Log.showDebug(TAG, `onMenuClick item remove: ${JSON.stringify(appInfo)}`); 242 const cacheKey = appInfo.appLabelId + appInfo.bundleName + appInfo.moduleName; 243 appInfo.keyName = appInfo.bundleName + appInfo.abilityName + appInfo.moduleName; 244 const appName = this.mSmartDockModel.getAppName(cacheKey); 245 Log.showDebug(TAG, `onMenuClick item remove appName: ${appName}`); 246 if (appName != null) { 247 appInfo.appName = appName; 248 } 249 this.mSelectedItem = appInfo; 250 this.mSelectedDockType = dockType; 251 AppStorage.setOrCreate('uninstallAppInfo', appInfo); 252 callback(); 253 }; 254 removeMenu.menuEnabled = appInfo.isUninstallAble; 255 menuInfoList.push(removeMenu); 256 } 257 return menuInfoList; 258 } 259 260 deleteDockItem(dockItem: DockItemInfo, dockType: number) { 261 this.mSmartDockModel.deleteDockItem(dockItem, dockType); 262 } 263 264 getSelectedItem(): DockItemInfo | null { 265 Log.showDebug(TAG, `getSelectedItem: ${JSON.stringify(this.mSelectedItem)}`); 266 return this.mSelectedItem; 267 } 268 269 getSelectedDockType(): number { 270 Log.showDebug(TAG, `getSelectedDockType: ${JSON.stringify(this.mSelectedDockType)}`); 271 return this.mSelectedDockType; 272 } 273 274 /** 275 * calcaulate dock list width after list change 276 * @param itemList 277 */ 278 private getListWidth(itemList: RecentBundleMissionInfo[]): number { 279 let width = 0; 280 if (typeof itemList === 'undefined' || itemList == null || itemList.length === 0) { 281 return width; 282 } 283 const num = itemList.length; 284 width = this.mSmartDockStyleConfig.mDockPadding * 2 + num * (this.mSmartDockStyleConfig.mListItemWidth) + (num - 1) * (this.mSmartDockStyleConfig.mListItemGap); 285 return width; 286 } 287 288 /** 289 * get snapshot 290 * 291 * @param missionIds missionid list 292 * @return snapshot list 293 */ 294 async getSnapshot(missionIds: MissionInfo[], name: string) { 295 const snapshotList: { 296 name: string, 297 image: any, 298 missionId: number, 299 boxSize: number, 300 bundleName: string, 301 left?: number, 302 right?: number, 303 }[] = await this.mSmartDockModel.getSnapshot(missionIds, name); 304 return snapshotList; 305 } 306 307 /** 308 * set start app info 309 */ 310 private setStartAppInfo(item: StartAppItemInfo) { 311 if (CheckEmptyUtils.isEmpty(item)) { 312 Log.showError(TAG, 'setStartAppInfo with item') 313 return; 314 } 315 item.icon = ResourceManager.getInstance().getCachedAppIcon(item.appIconId, item.bundleName, item.moduleName) 316 AppStorage.setOrCreate('startAppItemInfo', item); 317 this.mSmartDockStartAppHandler.setAppIconSize(this.mSmartDockStyleConfig.mIconSize); 318 this.mSmartDockStartAppHandler.setAppIconInfo(); 319 } 320}