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 { ShortcutInfo } from 'bundle/shortcutInfo'; 17import { Log } from '../utils/Log'; 18import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'; 19import { EventConstants } from '../constants/EventConstants'; 20import { CommonConstants } from '../constants/CommonConstants'; 21import { FormModel } from './FormModel'; 22import { AppItemInfo } from '../bean/AppItemInfo'; 23import { localEventManager } from '../manager/LocalEventManager'; 24import { launcherAbilityManager } from '../manager/LauncherAbilityManager'; 25import SystemApplication from '../configs/SystemApplication'; 26 27const TAG = 'AppModel'; 28 29/** 30 * Desktop application information data model. 31 */ 32export class AppModel { 33 private mBundleInfoList: AppItemInfo[] = []; 34 private readonly mSystemApplicationName = []; 35 private readonly mAppStateChangeListener = []; 36 private readonly mShortcutInfoMap = new Map<string, ShortcutInfo[]>(); 37 private readonly mFormModel: FormModel; 38 private readonly mInstallationListener; 39 40 private constructor() { 41 Log.showInfo(TAG, 'constructor start'); 42 this.mSystemApplicationName = SystemApplication.SystemApplicationName.split(','); 43 this.mFormModel = FormModel.getInstance(); 44 this.mInstallationListener = this.installationSubscriberCallBack.bind(this); 45 } 46 47 /** 48 * Get the application data model object. 49 * 50 * @return {object} application data model singleton 51 */ 52 static getInstance(): AppModel { 53 if (globalThis.AppModel == null) { 54 globalThis.AppModel = new AppModel(); 55 } 56 return globalThis.AppModel; 57 } 58 59 /** 60 * Get the list of apps displayed on the desktop. 61 * (public function, reduce the frequency of method call) 62 * 63 * @return {array} bundleInfoList 64 */ 65 async getAppList() { 66 Log.showInfo(TAG, 'getAppList start'); 67 if (!CheckEmptyUtils.isEmptyArr(this.mBundleInfoList)) { 68 Log.showInfo(TAG, `getAppList bundleInfoList length: ${this.mBundleInfoList.length}`); 69 return this.mBundleInfoList; 70 } 71 const bundleInfoList: AppItemInfo[] = await this.getAppListAsync(); 72 return bundleInfoList; 73 } 74 75 /** 76 * Get the list of apps displayed on the desktop (private function). 77 * 78 * @return {array} bundleInfoList, excluding system applications 79 */ 80 async getAppListAsync(): Promise<AppItemInfo[]> { 81 let allAbilityList: AppItemInfo[] = await launcherAbilityManager.getLauncherAbilityList(); 82 Log.showInfo(TAG, `getAppListAsync--->allAbilityList length: ${allAbilityList.length}`); 83 let launcherAbilityList: AppItemInfo[] = []; 84 for (let i in allAbilityList) { 85 if (this.mSystemApplicationName.indexOf(allAbilityList[i].bundleName) === CommonConstants.INVALID_VALUE) { 86 launcherAbilityList.push(allAbilityList[i]); 87 this.updateShortcutInfo(allAbilityList[i].bundleName); 88 this.mFormModel.updateAppItemFormInfo(allAbilityList[i].bundleName); 89 } 90 } 91 this.mBundleInfoList = launcherAbilityList; 92 Log.showInfo(TAG, `getAppListAsync--->allAbilityList length after filtration: ${launcherAbilityList.length}`); 93 return launcherAbilityList; 94 } 95 96 /** 97 * Register application list change event listener. 98 * 99 * @param listener 100 */ 101 registerStateChangeListener(listener): void { 102 if (this.mAppStateChangeListener.indexOf(listener) === CommonConstants.INVALID_VALUE) { 103 this.mAppStateChangeListener.push(listener); 104 } 105 } 106 107 /** 108 * Unregister application list change event listener. 109 * 110 * @param listener 111 */ 112 unregisterAppStateChangeListener(listener): void { 113 let index: number = this.mAppStateChangeListener.indexOf(listener); 114 if (index != CommonConstants.INVALID_VALUE) { 115 this.mAppStateChangeListener.splice(index, 1); 116 } 117 } 118 119 getUserId(): number { 120 return launcherAbilityManager.getUserId(); 121 } 122 123 /** 124 * Start listening to the system application status. 125 */ 126 registerAppListEvent(): void { 127 launcherAbilityManager.registerLauncherAbilityChangeListener(this.mInstallationListener); 128 } 129 130 /** 131 * Stop listening for system application status. 132 */ 133 unregisterAppListEvent(): void { 134 launcherAbilityManager.unregisterLauncherAbilityChangeListener(this.mInstallationListener); 135 } 136 137 /** 138 * The callback function of the application installation event. 139 * 140 * @param {Object} event 141 * @param {string} bundleName 142 * @param {number} userId 143 */ 144 private async installationSubscriberCallBack(event, bundleName, userId) { 145 Log.showInfo(TAG, `installationSubscriberCallBack event: ${event}`); 146 this.closePopup(); 147 this.updateShortcutInfo(bundleName, event); 148 this.mFormModel.updateAppItemFormInfo(bundleName, event); 149 if (event === EventConstants.EVENT_PACKAGE_REMOVED) { 150 this.removeItem(bundleName); 151 this.mFormModel.deleteFormByBundleName(bundleName); 152 153 // delete app from folder 154 localEventManager.sendLocalEventSticky(EventConstants.EVENT_FOLDER_PACKAGE_REMOVED, bundleName); 155 156 // delete app form dock 157 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, { 158 bundleName: bundleName, 159 keyName: undefined 160 }); 161 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECENT_DOCK_ITEM_DELETE, { 162 bundleName: bundleName, 163 keyName: undefined 164 }); 165 166 // delete app from pageDesktop 167 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_DELETE, { 168 bundleName: bundleName, 169 keyName: undefined 170 }); 171 } else { 172 const abilityInfos = await launcherAbilityManager.getLauncherAbilityInfo(bundleName); 173 Log.showDebug(TAG, `installationSubscriberCallBack abilityInfos: ${JSON.stringify(abilityInfos)}`); 174 if (!CheckEmptyUtils.isEmptyArr(abilityInfos)){ 175 this.replaceItem(bundleName, abilityInfos); 176 } 177 if (event === EventConstants.EVENT_PACKAGE_CHANGED) { 178 await this.getAppListAsync(); 179 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_UPDATE, null); 180 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_UPDATE, abilityInfos[0]); 181 } 182 } 183 this.notifyAppStateChangeEvent(); 184 } 185 186 /** 187 * Send event about application state change. 188 */ 189 private notifyAppStateChangeEvent() { 190 for (let i = 0; i < this.mAppStateChangeListener.length; i++) { 191 this.mAppStateChangeListener[i](this.mBundleInfoList); 192 } 193 } 194 195 /** 196 * Get the app index in bundleInfoList. 197 * 198 * @param {string} bundleName 199 * @return {number} index 200 */ 201 private getItemIndex(bundleName): number { 202 for (const listItem of this.mBundleInfoList) { 203 if (listItem.bundleName === bundleName) { 204 const index = this.mBundleInfoList.indexOf(listItem); 205 return index; 206 } 207 } 208 return CommonConstants.INVALID_VALUE; 209 } 210 211 /** 212 * Append app items into the bundleInfoList. 213 * 214 * @param {array} abilityInfos 215 */ 216 private appendItem(abilityInfos): void { 217 for (let index = 0; index < abilityInfos.length; index++) { 218 this.mBundleInfoList.push(abilityInfos[index]); 219 } 220 } 221 222 /** 223 * Remove app item from the bundleInfoList. 224 * 225 * @param {string} bundleName 226 */ 227 private removeItem(bundleName: string): void { 228 Log.showDebug(TAG, `removeItem bundleName: ${bundleName}`); 229 let originItemIndex = this.getItemIndex(bundleName); 230 while (originItemIndex != CommonConstants.INVALID_VALUE) { 231 this.removeItemCache(this.mBundleInfoList[originItemIndex]); 232 this.mBundleInfoList.splice(originItemIndex, 1); 233 originItemIndex = this.getItemIndex(bundleName); 234 } 235 } 236 237 /** 238 * Remove app item from the cache. 239 * 240 * @param {string} bundleName 241 */ 242 private removeItemCache(appItemInfo: AppItemInfo): void { 243 Log.showDebug(TAG, `removeItemCache bundleName: ${(appItemInfo.bundleName)}`); 244 let cacheKey = appItemInfo.appLabelId + appItemInfo.bundleName + appItemInfo.moduleName; 245 globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'name'); 246 cacheKey = appItemInfo.appIconId + appItemInfo.bundleName + appItemInfo.moduleName; 247 globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'icon'); 248 } 249 250 /** 251 * Replace app items in the bundleInfoList. 252 * 253 * @param {string} bundleName 254 * @param {array} abilityInfos 255 */ 256 private replaceItem(bundleName: string, abilityInfos): void { 257 Log.showDebug(TAG, `replaceItem bundleName: ${bundleName}`); 258 this.removeItem(bundleName); 259 this.appendItem(abilityInfos); 260 } 261 262 /** 263 * Put shortcut info into map. 264 * 265 * @param {string} bundleName 266 * @param {array} shortcutInfo 267 */ 268 setShortcutInfo(bundleName: string, shortcutInfo: ShortcutInfo[]): void { 269 this.mShortcutInfoMap.set(bundleName, shortcutInfo); 270 } 271 272 /** 273 * Get shortcut info from map. 274 * 275 * @param {string} bundleName 276 * @return {array | undefined} shortcutInfo 277 */ 278 getShortcutInfo(bundleName: string): ShortcutInfo[] | undefined { 279 return this.mShortcutInfoMap.get(bundleName); 280 } 281 282 /** 283 * Update shortcut info of map. 284 * 285 * @param {string} bundleName 286 * @param {string | undefined} eventType 287 */ 288 private updateShortcutInfo(bundleName, eventType?): void { 289 if (eventType && eventType === EventConstants.EVENT_PACKAGE_REMOVED) { 290 this.mShortcutInfoMap.delete(bundleName); 291 return; 292 } 293 launcherAbilityManager.getShortcutInfo(bundleName, this.setShortcutInfo.bind(this)); 294 } 295 296 /** 297 * Close popup. 298 */ 299 private closePopup(): void { 300 ContextMenu.close(); 301 } 302}