16e80583aSopenharmony_ci/**
26e80583aSopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
36e80583aSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
46e80583aSopenharmony_ci * you may not use this file except in compliance with the License.
56e80583aSopenharmony_ci * You may obtain a copy of the License at
66e80583aSopenharmony_ci *
76e80583aSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
86e80583aSopenharmony_ci *
96e80583aSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
106e80583aSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
116e80583aSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
126e80583aSopenharmony_ci * See the License for the specific language governing permissions and
136e80583aSopenharmony_ci * limitations under the License.
146e80583aSopenharmony_ci */
156e80583aSopenharmony_ci
166e80583aSopenharmony_ciimport { Log } from '../utils/Log';
176e80583aSopenharmony_ciimport { CheckEmptyUtils } from '../utils/CheckEmptyUtils';
186e80583aSopenharmony_ciimport { EventConstants } from '../constants/EventConstants';
196e80583aSopenharmony_ciimport { CommonConstants } from '../constants/CommonConstants';
206e80583aSopenharmony_ciimport { FormModel } from './FormModel';
216e80583aSopenharmony_ciimport { AppItemInfo } from '../bean/AppItemInfo';
226e80583aSopenharmony_ciimport { localEventManager } from '../manager/LocalEventManager';
236e80583aSopenharmony_ciimport { launcherAbilityManager } from '../manager/LauncherAbilityManager';
246e80583aSopenharmony_ciimport SystemApplication from '../configs/SystemApplication';
256e80583aSopenharmony_ciimport { AtomicServiceAppModel } from './AtomicServiceAppModel';
266e80583aSopenharmony_ciimport launcherBundleManager from '@ohos.bundle.launcherBundleManager';
276e80583aSopenharmony_ci
286e80583aSopenharmony_ciconst TAG = 'AppModel';
296e80583aSopenharmony_ci
306e80583aSopenharmony_ci/**
316e80583aSopenharmony_ci * Desktop application information data model.
326e80583aSopenharmony_ci */
336e80583aSopenharmony_ciexport class AppModel {
346e80583aSopenharmony_ci  private mBundleInfoList: AppItemInfo[] = [];
356e80583aSopenharmony_ci  private readonly mSystemApplicationName = [];
366e80583aSopenharmony_ci  private readonly mAppStateChangeListener = [];
376e80583aSopenharmony_ci  private readonly mShortcutInfoMap = new Map<string, launcherBundleManager.ShortcutInfo[]>();
386e80583aSopenharmony_ci  private readonly mFormModel: FormModel;
396e80583aSopenharmony_ci  private readonly mInstallationListener;
406e80583aSopenharmony_ci  private readonly mAtomicServiceAppModel: AtomicServiceAppModel;
416e80583aSopenharmony_ci
426e80583aSopenharmony_ci  private constructor() {
436e80583aSopenharmony_ci    Log.showInfo(TAG, 'constructor start');
446e80583aSopenharmony_ci    this.mSystemApplicationName = SystemApplication.SystemApplicationName.split(',');
456e80583aSopenharmony_ci    this.mFormModel = FormModel.getInstance();
466e80583aSopenharmony_ci    this.mAtomicServiceAppModel = AtomicServiceAppModel.getInstance();
476e80583aSopenharmony_ci    this.mInstallationListener = this.installationSubscriberCallBack.bind(this);
486e80583aSopenharmony_ci  }
496e80583aSopenharmony_ci
506e80583aSopenharmony_ci  /**
516e80583aSopenharmony_ci   * Get the application data model object.
526e80583aSopenharmony_ci   *
536e80583aSopenharmony_ci   * @return {object} application data model singleton
546e80583aSopenharmony_ci   */
556e80583aSopenharmony_ci  static getInstance(): AppModel {
566e80583aSopenharmony_ci    if (globalThis.AppModel == null) {
576e80583aSopenharmony_ci      globalThis.AppModel = new AppModel();
586e80583aSopenharmony_ci    }
596e80583aSopenharmony_ci    return globalThis.AppModel;
606e80583aSopenharmony_ci  }
616e80583aSopenharmony_ci
626e80583aSopenharmony_ci  /**
636e80583aSopenharmony_ci   * Get the list of apps displayed on the desktop.
646e80583aSopenharmony_ci   * (public function, reduce the frequency of method call)
656e80583aSopenharmony_ci   *
666e80583aSopenharmony_ci   * @return {array} bundleInfoList
676e80583aSopenharmony_ci   */
686e80583aSopenharmony_ci  async getAppList() {
696e80583aSopenharmony_ci    Log.showInfo(TAG, 'getAppList start');
706e80583aSopenharmony_ci    if (!CheckEmptyUtils.isEmptyArr(this.mBundleInfoList)) {
716e80583aSopenharmony_ci      Log.showInfo(TAG, `getAppList bundleInfoList length: ${this.mBundleInfoList.length}`);
726e80583aSopenharmony_ci      return this.mBundleInfoList;
736e80583aSopenharmony_ci    }
746e80583aSopenharmony_ci    const bundleInfoList: AppItemInfo[] = await this.getAppListAsync();
756e80583aSopenharmony_ci    return bundleInfoList;
766e80583aSopenharmony_ci  }
776e80583aSopenharmony_ci
786e80583aSopenharmony_ci  /**
796e80583aSopenharmony_ci   * Get the list of apps displayed on the desktop (private function).
806e80583aSopenharmony_ci   *
816e80583aSopenharmony_ci   * @return {array} bundleInfoList, excluding system applications
826e80583aSopenharmony_ci   */
836e80583aSopenharmony_ci  async getAppListAsync(): Promise<AppItemInfo[]> {
846e80583aSopenharmony_ci    let allAbilityList: AppItemInfo[] = await launcherAbilityManager.getLauncherAbilityList();
856e80583aSopenharmony_ci    Log.showInfo(TAG, `getAppListAsync--->allAbilityList length: ${allAbilityList.length}`);
866e80583aSopenharmony_ci    let launcherAbilityList: AppItemInfo[] = [];
876e80583aSopenharmony_ci    for (const ability of allAbilityList) {
886e80583aSopenharmony_ci      if (this.mSystemApplicationName.indexOf(ability.bundleName) === CommonConstants.INVALID_VALUE) {
896e80583aSopenharmony_ci        launcherAbilityList.push(ability);
906e80583aSopenharmony_ci        this.updateShortcutInfo(ability.bundleName);
916e80583aSopenharmony_ci        this.mFormModel.updateAppItemFormInfo(ability.bundleName);
926e80583aSopenharmony_ci      }
936e80583aSopenharmony_ci    }
946e80583aSopenharmony_ci    this.mBundleInfoList = launcherAbilityList;
956e80583aSopenharmony_ci    Log.showInfo(TAG, `getAppListAsync--->allAbilityList length after filtration: ${launcherAbilityList.length}`);
966e80583aSopenharmony_ci    return launcherAbilityList;
976e80583aSopenharmony_ci  }
986e80583aSopenharmony_ci
996e80583aSopenharmony_ci  /**
1006e80583aSopenharmony_ci   * Register application list change event listener.
1016e80583aSopenharmony_ci   *
1026e80583aSopenharmony_ci   * @param listener
1036e80583aSopenharmony_ci   */
1046e80583aSopenharmony_ci  registerStateChangeListener(listener): void {
1056e80583aSopenharmony_ci    if (this.mAppStateChangeListener.indexOf(listener) === CommonConstants.INVALID_VALUE) {
1066e80583aSopenharmony_ci      this.mAppStateChangeListener.push(listener);
1076e80583aSopenharmony_ci    }
1086e80583aSopenharmony_ci  }
1096e80583aSopenharmony_ci
1106e80583aSopenharmony_ci  /**
1116e80583aSopenharmony_ci   * Unregister application list change event listener.
1126e80583aSopenharmony_ci   *
1136e80583aSopenharmony_ci   * @param listener
1146e80583aSopenharmony_ci   */
1156e80583aSopenharmony_ci  unregisterAppStateChangeListener(listener): void {
1166e80583aSopenharmony_ci    let index: number = this.mAppStateChangeListener.indexOf(listener);
1176e80583aSopenharmony_ci    if (index != CommonConstants.INVALID_VALUE) {
1186e80583aSopenharmony_ci      this.mAppStateChangeListener.splice(index, 1);
1196e80583aSopenharmony_ci    }
1206e80583aSopenharmony_ci  }
1216e80583aSopenharmony_ci
1226e80583aSopenharmony_ci  getUserId(): number {
1236e80583aSopenharmony_ci    return launcherAbilityManager.getUserId();
1246e80583aSopenharmony_ci  }
1256e80583aSopenharmony_ci
1266e80583aSopenharmony_ci  /**
1276e80583aSopenharmony_ci   * Start listening to the system application status.
1286e80583aSopenharmony_ci   */
1296e80583aSopenharmony_ci  registerAppListEvent(): void {
1306e80583aSopenharmony_ci    launcherAbilityManager.registerLauncherAbilityChangeListener(this.mInstallationListener);
1316e80583aSopenharmony_ci  }
1326e80583aSopenharmony_ci
1336e80583aSopenharmony_ci  /**
1346e80583aSopenharmony_ci   * Stop listening for system application status.
1356e80583aSopenharmony_ci   */
1366e80583aSopenharmony_ci  unregisterAppListEvent(): void {
1376e80583aSopenharmony_ci    launcherAbilityManager.unregisterLauncherAbilityChangeListener(this.mInstallationListener);
1386e80583aSopenharmony_ci  }
1396e80583aSopenharmony_ci
1406e80583aSopenharmony_ci  /**
1416e80583aSopenharmony_ci   * The callback function of the application installation event.
1426e80583aSopenharmony_ci   *
1436e80583aSopenharmony_ci   * @param {Object} event
1446e80583aSopenharmony_ci   * @param {string} bundleName
1456e80583aSopenharmony_ci   * @param {number} userId
1466e80583aSopenharmony_ci   */
1476e80583aSopenharmony_ci  private async installationSubscriberCallBack(event, bundleName, userId) {
1486e80583aSopenharmony_ci    Log.showInfo(TAG, `installationSubscriberCallBack event: ${event}`);
1496e80583aSopenharmony_ci    this.closePopup();
1506e80583aSopenharmony_ci    this.updateShortcutInfo(bundleName, event);
1516e80583aSopenharmony_ci    this.mFormModel.updateAppItemFormInfo(bundleName, event);
1526e80583aSopenharmony_ci    // initial mBundleInfoList
1536e80583aSopenharmony_ci    if (CheckEmptyUtils.isEmptyArr(this.mBundleInfoList)) {
1546e80583aSopenharmony_ci      await this.getAppListAsync();
1556e80583aSopenharmony_ci    }
1566e80583aSopenharmony_ci    if (event === EventConstants.EVENT_PACKAGE_REMOVED) {
1576e80583aSopenharmony_ci      this.removeItem(bundleName);
1586e80583aSopenharmony_ci      this.mAtomicServiceAppModel.removeAtomicServiceItem(bundleName);
1596e80583aSopenharmony_ci      this.mFormModel.deleteFormByBundleName(bundleName);
1606e80583aSopenharmony_ci
1616e80583aSopenharmony_ci      // delete app from folder
1626e80583aSopenharmony_ci      localEventManager.sendLocalEventSticky(EventConstants.EVENT_FOLDER_PACKAGE_REMOVED, bundleName);
1636e80583aSopenharmony_ci
1646e80583aSopenharmony_ci      // delete app form dock
1656e80583aSopenharmony_ci      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, {
1666e80583aSopenharmony_ci        bundleName: bundleName,
1676e80583aSopenharmony_ci        keyName: undefined
1686e80583aSopenharmony_ci      });
1696e80583aSopenharmony_ci      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECENT_DOCK_ITEM_DELETE, {
1706e80583aSopenharmony_ci        bundleName: bundleName,
1716e80583aSopenharmony_ci        keyName: undefined
1726e80583aSopenharmony_ci      });
1736e80583aSopenharmony_ci
1746e80583aSopenharmony_ci      // delete app from pageDesktop
1756e80583aSopenharmony_ci      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_DELETE, {
1766e80583aSopenharmony_ci        bundleName: bundleName,
1776e80583aSopenharmony_ci        keyName: undefined
1786e80583aSopenharmony_ci      });
1796e80583aSopenharmony_ci
1806e80583aSopenharmony_ci      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECOMMEND_FORM_DELETE, bundleName);
1816e80583aSopenharmony_ci    } else {
1826e80583aSopenharmony_ci      let appItemInfo: AppItemInfo = await this.getAndReplaceLauncherAbility(bundleName);
1836e80583aSopenharmony_ci
1846e80583aSopenharmony_ci      if (CheckEmptyUtils.isEmpty(appItemInfo)) {
1856e80583aSopenharmony_ci        appItemInfo = await this.mAtomicServiceAppModel.getAndReplaceAtomicAbility(bundleName);
1866e80583aSopenharmony_ci      }
1876e80583aSopenharmony_ci
1886e80583aSopenharmony_ci      if (CheckEmptyUtils.isEmpty(appItemInfo)) {
1896e80583aSopenharmony_ci        Log.showError(TAG, `installationSubscriberCallBack neither launcher nor atomic app, bundleName:${bundleName} `);
1906e80583aSopenharmony_ci        return;
1916e80583aSopenharmony_ci      }
1926e80583aSopenharmony_ci      if (event === EventConstants.EVENT_PACKAGE_CHANGED) {
1936e80583aSopenharmony_ci        Log.showInfo(TAG, `installationSubscriber, PACKAGE_CHANGED, bundleName is ${bundleName}`);
1946e80583aSopenharmony_ci        AppStorage.setOrCreate('formRefresh', String(new Date()));
1956e80583aSopenharmony_ci
1966e80583aSopenharmony_ci        localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECOMMEND_FORM_UPDATE, bundleName);
1976e80583aSopenharmony_ci        Log.showError(TAG, `installationSubscriberCallBack unKnow bundleType:${appItemInfo.bundleType}`);
1986e80583aSopenharmony_ci      } else {
1996e80583aSopenharmony_ci        await this.mFormModel.updateAppItemFormInfo(bundleName);
2006e80583aSopenharmony_ci        await this.mFormModel.updateAtomicServiceAppItemFormInfo(bundleName);
2016e80583aSopenharmony_ci        localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RECOMMEND_FORM_ADD, bundleName);
2026e80583aSopenharmony_ci      }
2036e80583aSopenharmony_ci    }
2046e80583aSopenharmony_ci    this.notifyAppStateChangeEvent();
2056e80583aSopenharmony_ci  }
2066e80583aSopenharmony_ci
2076e80583aSopenharmony_ci  private async getAndReplaceLauncherAbility(bundleName: string): Promise<AppItemInfo> {
2086e80583aSopenharmony_ci    const abilityInfos: AppItemInfo[] = await launcherAbilityManager.getLauncherAbilityInfo(bundleName);
2096e80583aSopenharmony_ci    if (CheckEmptyUtils.isEmptyArr(abilityInfos)) {
2106e80583aSopenharmony_ci      return undefined;
2116e80583aSopenharmony_ci    }
2126e80583aSopenharmony_ci    Log.showDebug(TAG, `launcher abilityInfos: ${JSON.stringify(abilityInfos)}`);
2136e80583aSopenharmony_ci    this.replaceItem(bundleName, abilityInfos);
2146e80583aSopenharmony_ci    return abilityInfos[0];
2156e80583aSopenharmony_ci  }
2166e80583aSopenharmony_ci
2176e80583aSopenharmony_ci  /**
2186e80583aSopenharmony_ci   * Send event about application state change.
2196e80583aSopenharmony_ci   */
2206e80583aSopenharmony_ci  private notifyAppStateChangeEvent() {
2216e80583aSopenharmony_ci    for (let i = 0; i < this.mAppStateChangeListener.length; i++) {
2226e80583aSopenharmony_ci      this.mAppStateChangeListener[i](this.mBundleInfoList);
2236e80583aSopenharmony_ci    }
2246e80583aSopenharmony_ci  }
2256e80583aSopenharmony_ci
2266e80583aSopenharmony_ci  /**
2276e80583aSopenharmony_ci   * Get the app index in bundleInfoList.
2286e80583aSopenharmony_ci   *
2296e80583aSopenharmony_ci   * @param {string} bundleName
2306e80583aSopenharmony_ci   * @return {number} index
2316e80583aSopenharmony_ci   */
2326e80583aSopenharmony_ci  private getItemIndex(bundleName): number {
2336e80583aSopenharmony_ci    for (const listItem of this.mBundleInfoList) {
2346e80583aSopenharmony_ci      if (listItem.bundleName === bundleName) {
2356e80583aSopenharmony_ci        const index = this.mBundleInfoList.indexOf(listItem);
2366e80583aSopenharmony_ci        return index;
2376e80583aSopenharmony_ci      }
2386e80583aSopenharmony_ci    }
2396e80583aSopenharmony_ci    return CommonConstants.INVALID_VALUE;
2406e80583aSopenharmony_ci  }
2416e80583aSopenharmony_ci
2426e80583aSopenharmony_ci  /**
2436e80583aSopenharmony_ci   * Append app items into the bundleInfoList.
2446e80583aSopenharmony_ci   *
2456e80583aSopenharmony_ci   * @param {array} abilityInfos
2466e80583aSopenharmony_ci   */
2476e80583aSopenharmony_ci  private appendItem(abilityInfos): void {
2486e80583aSopenharmony_ci    for (let index = 0; index < abilityInfos.length; index++) {
2496e80583aSopenharmony_ci      this.mBundleInfoList.push(abilityInfos[index]);
2506e80583aSopenharmony_ci    }
2516e80583aSopenharmony_ci  }
2526e80583aSopenharmony_ci
2536e80583aSopenharmony_ci  /**
2546e80583aSopenharmony_ci   * Remove app item from the bundleInfoList.
2556e80583aSopenharmony_ci   *
2566e80583aSopenharmony_ci   * @param {string} bundleName
2576e80583aSopenharmony_ci   */
2586e80583aSopenharmony_ci  private removeItem(bundleName: string): void {
2596e80583aSopenharmony_ci    Log.showDebug(TAG, `removeItem bundleName: ${bundleName}`);
2606e80583aSopenharmony_ci    let originItemIndex = this.getItemIndex(bundleName);
2616e80583aSopenharmony_ci    while (originItemIndex != CommonConstants.INVALID_VALUE) {
2626e80583aSopenharmony_ci      this.removeItemCache(this.mBundleInfoList[originItemIndex]);
2636e80583aSopenharmony_ci      this.mBundleInfoList.splice(originItemIndex, 1);
2646e80583aSopenharmony_ci      originItemIndex = this.getItemIndex(bundleName);
2656e80583aSopenharmony_ci    }
2666e80583aSopenharmony_ci  }
2676e80583aSopenharmony_ci
2686e80583aSopenharmony_ci  /**
2696e80583aSopenharmony_ci * Remove app item from the cache.
2706e80583aSopenharmony_ci *
2716e80583aSopenharmony_ci * @param {string} bundleName
2726e80583aSopenharmony_ci */
2736e80583aSopenharmony_ci  private removeItemCache(appItemInfo: AppItemInfo): void {
2746e80583aSopenharmony_ci    Log.showDebug(TAG, `removeItemCache bundleName: ${(appItemInfo.bundleName)}`);
2756e80583aSopenharmony_ci    let cacheKey = appItemInfo.appLabelId + appItemInfo.bundleName + appItemInfo.moduleName;
2766e80583aSopenharmony_ci    globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'name');
2776e80583aSopenharmony_ci    cacheKey = appItemInfo.appIconId + appItemInfo.bundleName + appItemInfo.moduleName;
2786e80583aSopenharmony_ci    globalThis.ResourceManager.deleteAppResourceCache(cacheKey, 'icon');
2796e80583aSopenharmony_ci  }
2806e80583aSopenharmony_ci
2816e80583aSopenharmony_ci  /**
2826e80583aSopenharmony_ci   * Replace app items in the bundleInfoList.
2836e80583aSopenharmony_ci   *
2846e80583aSopenharmony_ci   * @param {string} bundleName
2856e80583aSopenharmony_ci   * @param {array} abilityInfos
2866e80583aSopenharmony_ci   */
2876e80583aSopenharmony_ci  private replaceItem(bundleName: string, abilityInfos): void {
2886e80583aSopenharmony_ci    Log.showDebug(TAG, `replaceItem bundleName: ${bundleName}`);
2896e80583aSopenharmony_ci    this.removeItem(bundleName);
2906e80583aSopenharmony_ci    this.appendItem(abilityInfos);
2916e80583aSopenharmony_ci  }
2926e80583aSopenharmony_ci
2936e80583aSopenharmony_ci  /**
2946e80583aSopenharmony_ci   * Put shortcut info into map.
2956e80583aSopenharmony_ci   *
2966e80583aSopenharmony_ci   * @param {string} bundleName
2976e80583aSopenharmony_ci   * @param {array} shortcutInfo
2986e80583aSopenharmony_ci   */
2996e80583aSopenharmony_ci  setShortcutInfo(bundleName: string, shortcutInfo: launcherBundleManager.ShortcutInfo[]): void {
3006e80583aSopenharmony_ci    this.mShortcutInfoMap.set(bundleName, shortcutInfo);
3016e80583aSopenharmony_ci  }
3026e80583aSopenharmony_ci
3036e80583aSopenharmony_ci  /**
3046e80583aSopenharmony_ci   * Get shortcut info from map.
3056e80583aSopenharmony_ci   *
3066e80583aSopenharmony_ci   * @param {string} bundleName
3076e80583aSopenharmony_ci   * @return {array | undefined} shortcutInfo
3086e80583aSopenharmony_ci   */
3096e80583aSopenharmony_ci  getShortcutInfo(bundleName: string): launcherBundleManager.ShortcutInfo[] | undefined {
3106e80583aSopenharmony_ci    return this.mShortcutInfoMap.get(bundleName);
3116e80583aSopenharmony_ci  }
3126e80583aSopenharmony_ci
3136e80583aSopenharmony_ci  /**
3146e80583aSopenharmony_ci   * Update shortcut info of map.
3156e80583aSopenharmony_ci   *
3166e80583aSopenharmony_ci   * @param {string} bundleName
3176e80583aSopenharmony_ci   * @param {string | undefined} eventType
3186e80583aSopenharmony_ci   */
3196e80583aSopenharmony_ci  private updateShortcutInfo(bundleName, eventType?): void {
3206e80583aSopenharmony_ci    if (eventType && eventType === EventConstants.EVENT_PACKAGE_REMOVED) {
3216e80583aSopenharmony_ci      this.mShortcutInfoMap.delete(bundleName);
3226e80583aSopenharmony_ci      return;
3236e80583aSopenharmony_ci    }
3246e80583aSopenharmony_ci    launcherAbilityManager.getShortcutInfo(bundleName, this.setShortcutInfo.bind(this));
3256e80583aSopenharmony_ci  }
3266e80583aSopenharmony_ci
3276e80583aSopenharmony_ci  /**
3286e80583aSopenharmony_ci   * Close popup.
3296e80583aSopenharmony_ci   */
3306e80583aSopenharmony_ci  private closePopup(): void {
3316e80583aSopenharmony_ci    ContextMenu.close();
3326e80583aSopenharmony_ci  }
3336e80583aSopenharmony_ci}