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 // initial mBundleInfoList 150 if (CheckEmptyUtils.isEmptyArr(this.mBundleInfoList)) { 151 await this.getAppListAsync(); 152 } 153 if (event === EventConstants.EVENT_PACKAGE_REMOVED) { 154 this.removeItem(bundleName); 155 this.mFormModel.deleteFormByBundleName(bundleName); 156 157 // delete app from folder 158 localEventManager.sendLocalEventSticky(EventConstants.EVENT_FOLDER_PACKAGE_REMOVED, bundleName); 159 160 // delete app form dock 161 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, { 162 bundleName: bundleName, 163 keyName: undefined 164 }); 165 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECENT_DOCK_ITEM_DELETE, { 166 bundleName: bundleName, 167 keyName: undefined 168 }); 169 170 // delete app from pageDesktop 171 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_DELETE, { 172 bundleName: bundleName, 173 keyName: undefined 174 }); 175 } else { 176 const abilityInfos = await launcherAbilityManager.getLauncherAbilityInfo(bundleName); 177 Log.showDebug(TAG, `installationSubscriberCallBack abilityInfos: ${JSON.stringify(abilityInfos)}`); 178 if (!CheckEmptyUtils.isEmptyArr(abilityInfos)){ 179 this.replaceItem(bundleName, abilityInfos); 180 } 181 if (event === EventConstants.EVENT_PACKAGE_CHANGED) { 182 await this.getAppListAsync(); 183 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_UPDATE, null); 184 localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_UPDATE, abilityInfos[0]); 185 } 186 } 187 this.notifyAppStateChangeEvent(); 188 } 189 190 /** 191 * Send event about application state change. 192 */ 193 private notifyAppStateChangeEvent() { 194 for (let i = 0; i < this.mAppStateChangeListener.length; i++) { 195 this.mAppStateChangeListener[i](this.mBundleInfoList); 196 } 197 } 198 199 /** 200 * Get the app index in bundleInfoList. 201 * 202 * @param {string} bundleName 203 * @return {number} index 204 */ 205 private getItemIndex(bundleName): number { 206 for (const listItem of this.mBundleInfoList) { 207 if (listItem.bundleName === bundleName) { 208 const index = this.mBundleInfoList.indexOf(listItem); 209 return index; 210 } 211 } 212 return CommonConstants.INVALID_VALUE; 213 } 214 215 /** 216 * Append app items into the bundleInfoList. 217 * 218 * @param {array} abilityInfos 219 */ 220 private appendItem(abilityInfos): void { 221 for (let index = 0; index < abilityInfos.length; index++) { 222 this.mBundleInfoList.push(abilityInfos[index]); 223 } 224 } 225 226 /** 227 * Remove app item from the bundleInfoList. 228 * 229 * @param {string} bundleName 230 */ 231 private removeItem(bundleName: string): void { 232 Log.showDebug(TAG, `removeItem bundleName: ${bundleName}`); 233 let originItemIndex = this.getItemIndex(bundleName); 234 while (originItemIndex != CommonConstants.INVALID_VALUE) { 235 this.removeItemCache(this.mBundleInfoList[originItemIndex]); 236 this.mBundleInfoList.splice(originItemIndex, 1); 237 originItemIndex = this.getItemIndex(bundleName); 238 } 239 } 240 241 /** 242 * Remove app item from the cache. 243 * 244 * @param {string} bundleName 245 */ 246 private removeItemCache(appItemInfo: AppItemInfo): void { 247 Log.showDebug(TAG, `removeItemCache bundleName: ${(appItemInfo.bundleName)}`); 248 let cacheKey = appItemInfo.appLabelId + appItemInfo.bundleName + appItemInfo.moduleName; 249 globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'name'); 250 cacheKey = appItemInfo.appIconId + appItemInfo.bundleName + appItemInfo.moduleName; 251 globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'icon'); 252 } 253 254 /** 255 * Replace app items in the bundleInfoList. 256 * 257 * @param {string} bundleName 258 * @param {array} abilityInfos 259 */ 260 private replaceItem(bundleName: string, abilityInfos): void { 261 Log.showDebug(TAG, `replaceItem bundleName: ${bundleName}`); 262 this.removeItem(bundleName); 263 this.appendItem(abilityInfos); 264 } 265 266 /** 267 * Put shortcut info into map. 268 * 269 * @param {string} bundleName 270 * @param {array} shortcutInfo 271 */ 272 setShortcutInfo(bundleName: string, shortcutInfo: ShortcutInfo[]): void { 273 this.mShortcutInfoMap.set(bundleName, shortcutInfo); 274 } 275 276 /** 277 * Get shortcut info from map. 278 * 279 * @param {string} bundleName 280 * @return {array | undefined} shortcutInfo 281 */ 282 getShortcutInfo(bundleName: string): ShortcutInfo[] | undefined { 283 return this.mShortcutInfoMap.get(bundleName); 284 } 285 286 /** 287 * Update shortcut info of map. 288 * 289 * @param {string} bundleName 290 * @param {string | undefined} eventType 291 */ 292 private updateShortcutInfo(bundleName, eventType?): void { 293 if (eventType && eventType === EventConstants.EVENT_PACKAGE_REMOVED) { 294 this.mShortcutInfoMap.delete(bundleName); 295 return; 296 } 297 launcherAbilityManager.getShortcutInfo(bundleName, this.setShortcutInfo.bind(this)); 298 } 299 300 /** 301 * Close popup. 302 */ 303 private closePopup(): void { 304 ContextMenu.close(); 305 } 306}