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 { Log } from '../utils/Log';
17import { EventConstants } from '../constants/EventConstants';
18import { CommonConstants } from '../constants/CommonConstants';
19import { CardItemInfo } from '../bean/CardItemInfo';
20import { SettingsModel } from './SettingsModel';
21import { FormManager } from '../manager/FormManager';
22import { RdbStoreManager } from '../manager/RdbStoreManager';
23import { FormListInfoCacheManager } from '../cache/FormListInfoCacheManager';
24import {PageDesktopModel} from './PageDesktopModel';
25import { GridLayoutInfo } from '../interface';
26
27
28const TAG = 'FormModel';
29
30/**
31 * form model.
32 */
33export class FormModel {
34  private readonly mRdbStoreManager: RdbStoreManager;
35  private readonly mFormManager: FormManager;
36  private readonly mFormListInfoCacheManager: FormListInfoCacheManager;
37  private readonly mAppItemFormInfoMap = new Map<string, CardItemInfo[]>();
38  private readonly mPageDesktopModel: PageDesktopModel;
39  private readonly mAtomicServiceAppItemFormInfoMap = new Map<string, CardItemInfo[]>();
40  private readonly KEY_FORM_LIST: string = 'formListInfo';
41
42  private constructor() {
43    this.mRdbStoreManager = RdbStoreManager.getInstance();
44    this.mFormManager = FormManager.getInstance();
45    this.mFormListInfoCacheManager = FormListInfoCacheManager.getInstance();
46    this.mPageDesktopModel = PageDesktopModel.getInstance();
47  }
48
49  /**
50   * Get the form model object.
51   *
52   * @return {object} form model singleton
53   */
54  static getInstance(): FormModel {
55    if (globalThis.FormModelInstance == null) {
56      globalThis.FormModelInstance = new FormModel();
57    }
58    return globalThis.FormModelInstance;
59  }
60
61  /**
62   * Get the form info list of all ohos applications on the device.
63   *
64   * @return {array} allFormList
65   */
66  async getAllFormsInfo() {
67    Log.showDebug(TAG, 'getAllFormsInfo start');
68    const allFormList = await this.mFormManager.getAllFormsInfo();
69    return allFormList;
70  }
71
72  getAllAppItemFormInfoMap(): Map<string, CardItemInfo[]> {
73    return this.mAppItemFormInfoMap;
74  }
75
76  /**
77   * Get the form info list of all ohos applications on the device by bundle name.
78   *
79   * @param {array} bundleName
80   * @param {function | undefined} callback
81   *
82   * @return {array} currentBundleFormsInfo
83   */
84  async getFormsInfoByBundleName(bundleName: string, callback?) {
85    Log.showDebug(TAG, `getFormsInfoByBundleName bundleName: ${bundleName}`);
86    let currentBundleFormsInfo: CardItemInfo[];
87    try {
88      currentBundleFormsInfo = await this.mFormManager.getFormsInfoByApp(bundleName);
89      if (callback != undefined) {
90        callback(bundleName, currentBundleFormsInfo);
91      }
92      AppStorage.setOrCreate('formMgrItem', currentBundleFormsInfo);
93      return currentBundleFormsInfo;
94    } catch (err) {
95      Log.showError(TAG, `getFormsInfoByBundleName bundleName:${bundleName} err: ${JSON.stringify(err)}`);
96    }
97  }
98
99  /**
100   * Get the form info list of all ohos applications on the device by module name.
101   *
102   * @param {string} bundleName
103   * @param {string} moduleName
104   *
105   * @return {array} currentModuleFormsInfo
106   */
107  async getFormsInfoByModuleName(bundleName: string, moduleName: string) {
108    Log.showDebug(TAG, `getFormsInfoByModuleName bundleName: ${bundleName}, moduleName: ${moduleName}`);
109    const currentModuleFormsInfo = await this.mFormManager.getFormsInfoByModule(bundleName, moduleName);
110    return currentModuleFormsInfo;
111  }
112
113  /**
114   * Get the form info list from rdb.
115   *
116   * @return {array} allFormList
117   */
118  async getAllFormsInfoFromRdb() {
119    Log.showDebug(TAG, 'getAllFormsInfoFromRdb start');
120    const allFormList = await this.mRdbStoreManager.getAllFormInfos();
121    return allFormList;
122  }
123
124  /**
125   * Update the form info in rdb by id.
126   *
127   * @param {object} cardItemInfo
128   *
129   * @return {boolean} result
130   */
131  async updateFormInfoById(cardItemInfo) {
132    return await this.mRdbStoreManager.updateFormInfoById(cardItemInfo);
133  }
134
135  /**
136   * Delete form in rdb and fms by id.
137   *
138   * @param {number} cardId
139   */
140  async deleteFormById(cardId: number): Promise<boolean> {
141    try{
142      await this.mRdbStoreManager.deleteFormInfoById(cardId);
143      await this.mFormManager.deleteCard(cardId.toString());
144    } catch(error){
145      Log.showError(TAG, `deleteFormById err: ${JSON.stringify(error)}`);
146      return false;
147    }
148    return true;
149
150  }
151
152  /**
153   * Delete form in fms by formId.
154   *
155   * @param {number} formId
156   */
157  deleteFormByFormID(formId: number) {
158    this.mFormManager.deleteCard(formId.toString());
159  }
160
161  /**
162   * Set app item form info into map.
163   *
164   * @param {string} bundleName
165   * @param {array} appItemFormInfo
166   */
167  setAppItemFormInfo(bundleName: string, appItemFormInfo: CardItemInfo[]): void {
168    this.mAppItemFormInfoMap.set(bundleName, appItemFormInfo);
169  }
170
171  /**
172   * Get app item form info from map.
173   *
174   * @param {string} bundleName
175   *
176   * @return {array | undefined} mAppItemFormInfoMap
177   */
178  getAppItemFormInfo(bundleName: string, moduleName?: string): CardItemInfo[] | undefined {
179    Log.showDebug(TAG, `getAppItemFormInfo bundleName: ${bundleName},
180      appItemFormInfo: ${JSON.stringify(this.mAppItemFormInfoMap.get(bundleName))}`);
181    let formList = this.mAppItemFormInfoMap.get(bundleName);
182    if (formList && formList.length && moduleName) {
183      formList = formList.filter(item => item.moduleName === moduleName);
184    }
185    return formList;
186  }
187
188  /**
189   * Update app item form info into map.
190   *
191   * @param {string} bundleName
192   * @param {string | undefined} eventType
193   */
194  async updateAppItemFormInfo(bundleName: string, eventType?: string): Promise<void> {
195    if (eventType && eventType === EventConstants.EVENT_PACKAGE_REMOVED) {
196      this.mAppItemFormInfoMap.delete(bundleName);
197      return;
198    }
199    this.getFormsInfoByBundleName(bundleName, this.setAppItemFormInfo.bind(this));
200  }
201
202  /**
203   * Delete form by bundleName and update layout info.
204   *
205   * @param {string} bundleName
206   */
207  deleteFormByBundleName(bundleName: string): void {
208    const settingsModel = SettingsModel.getInstance();
209    this.mRdbStoreManager.deleteFormInfoByBundle(bundleName);
210    const formInfoList: any = this.mFormListInfoCacheManager.getCache(this.KEY_FORM_LIST);
211    if (formInfoList === CommonConstants.INVALID_VALUE) {
212      return;
213    }
214    const layoutInfo = settingsModel.getLayoutInfo();
215    const tempFormInfoList = JSON.parse(JSON.stringify(formInfoList));
216    const pageItemMap = new Map<string, number>();
217    for (let i = 0; i < layoutInfo.layoutInfo.length; i++) {
218      const tmpPage = layoutInfo.layoutInfo[i].page.toString();
219      if (pageItemMap.has(tmpPage)) {
220        pageItemMap.set(tmpPage, pageItemMap.get(tmpPage) + 1);
221      } else {
222        pageItemMap.set(tmpPage, 1);
223      }
224    }
225    for(let i = formInfoList.length - 1; i >= 0; i--) {
226      const formInfo = formInfoList[i];
227      if (formInfo.bundleName === bundleName) {
228        tempFormInfoList.splice(i, 1);
229        for(let j = layoutInfo.layoutInfo.length - 1; j >= 0; j--) {
230          if (layoutInfo.layoutInfo[j].typeId === CommonConstants.TYPE_CARD && formInfo.cardId == layoutInfo.layoutInfo[j].cardId) {
231            const tmpPage = layoutInfo.layoutInfo[j].page.toString();
232            pageItemMap.set(tmpPage, pageItemMap.get(tmpPage) - 1);
233            layoutInfo.layoutInfo.splice(j, 1);
234            break;
235          }
236        }
237      }
238    }
239    if (tempFormInfoList.length === 0) {
240      this.mFormListInfoCacheManager.setCache(this.KEY_FORM_LIST, null);
241    } else {
242      this.mFormListInfoCacheManager.setCache(this.KEY_FORM_LIST, tempFormInfoList);
243    }
244
245    this.updateBlankPage(pageItemMap, layoutInfo);
246    settingsModel.setLayoutInfo(layoutInfo);
247  }
248
249  /**
250   * Delete form by cardId.
251   *
252   * @param {number} cardId.
253   */
254  async deleteForm(cardId) {
255    Log.showDebug(TAG, 'deleteForm start');
256    let gridLayoutInfo: GridLayoutInfo = {
257      layoutDescription: undefined,
258      layoutInfo: []
259    };
260    gridLayoutInfo = SettingsModel.getInstance().getLayoutInfo();
261    const cardIndex = gridLayoutInfo.layoutInfo.findIndex(item => {
262      return item.typeId === CommonConstants.TYPE_CARD && item.cardId === cardId;
263    });
264    if (cardIndex != CommonConstants.INVALID_VALUE) {
265      this.deleteFormById(cardId);
266      const page = gridLayoutInfo.layoutInfo[cardIndex].page;
267      gridLayoutInfo.layoutInfo.splice(cardIndex, 1);
268      let ret: boolean = this.mPageDesktopModel.deleteBlankPageFromLayoutInfo(gridLayoutInfo, page);
269      SettingsModel.getInstance().setLayoutInfo(gridLayoutInfo);
270      if(ret){
271        const curPageIndex = this.mPageDesktopModel.getPageIndex();
272        Log.showInfo(TAG, 'deleteForm' + curPageIndex);
273        this.mPageDesktopModel.setPageIndex(curPageIndex - 1);
274      }
275    }
276    const formInfoList: any = this.mFormListInfoCacheManager.getCache(this.KEY_FORM_LIST);
277    if (formInfoList === CommonConstants.INVALID_VALUE) {
278      return;
279    }
280    for(let i = 0; i < formInfoList.length; i++) {
281      if (formInfoList[i].cardId === cardId){
282        formInfoList.splice(i, 1);
283        break;
284      }
285    }
286    if (formInfoList.length === 0) {
287      this.mFormListInfoCacheManager.setCache(this.KEY_FORM_LIST, null);
288    } else {
289      this.mFormListInfoCacheManager.setCache(this.KEY_FORM_LIST, formInfoList);
290    }
291  }
292
293  /**
294   * update page number if blank page is exist
295   *
296   * @param pageItemMap
297   * @param layoutInfo
298   */
299  private updateBlankPage(pageItemMap, layoutInfo): void {
300    const blankPages = [];
301    for (let [page, count] of pageItemMap) {
302      if (count === 0) {
303        layoutInfo.layoutDescription.pageCount--;
304        blankPages.push(page);
305      }
306    }
307    for (let m = 0; m < layoutInfo.layoutInfo.length; m++) {
308      let pageMinus = 0;
309      for (let n = 0; n < blankPages.length; n++) {
310        if (layoutInfo.layoutInfo[m].page > blankPages[n]) {
311          pageMinus++;
312        }
313      }
314      if (pageMinus != 0) {
315        layoutInfo.layoutInfo[m].page = layoutInfo.layoutInfo[m].page - pageMinus;
316      }
317    }
318  }
319
320  private setAtomicServiceAppItemFormInfo(bundleName: string, appItemFormInfo: CardItemInfo[]): void {
321    this.mAtomicServiceAppItemFormInfoMap.set(bundleName, appItemFormInfo);
322  }
323
324  /**
325   * 获取所有原服务app 卡片信息
326   *
327   * @returns {Map<string, CardItemInfo[]>} mAtomicServiceAppItemFormInfoMap
328   */
329  getAllAtomicServiceAppItemFormInfoMap(): Map<string, CardItemInfo[]> {
330    return this.mAtomicServiceAppItemFormInfoMap;
331  }
332
333  /**
334   * 先从缓存中获取原服务卡片信息,没有的话,再从卡片管理器获取
335   *
336   * @param bundleName bundleName
337   * @returns {Promise<CardItemInfo[]>}
338   */
339  async getAtomicServiceFormsInfoFromMapAndManager(bundleName: string): Promise<CardItemInfo[]> {
340    let cardInfo: CardItemInfo[] = this.mAtomicServiceAppItemFormInfoMap.get(bundleName);
341    if (typeof cardInfo === 'undefined') {
342      return this.mFormManager.getFormsInfoByApp(bundleName)
343        .then(bundleFormsInfo => {
344          Log.showInfo(TAG, `getAtomicServiceFormsInfoFromMapAndManager bundleFormsInfo: ${JSON.stringify(bundleFormsInfo)}`);
345          return bundleFormsInfo;
346        })
347        .catch(err => {
348          Log.showError(TAG, `getAtomicServiceFormsInfoFromMapAndManager err: ${JSON.stringify(err)}`);
349          return [];
350        });
351    }
352    return cardInfo;
353  }
354
355  /**
356   * 更新原服务应用的卡片信息
357   *
358   * @param bundleName {string} bundleName
359   * @param eventType {string | undefined} eventType
360   * @returns {Promise<void>}
361   */
362  async updateAtomicServiceAppItemFormInfo(bundleName: string, eventType?: string): Promise<void> {
363    if (eventType && eventType === EventConstants.EVENT_PACKAGE_REMOVED) {
364      this.mAtomicServiceAppItemFormInfoMap.delete(bundleName);
365      return;
366    }
367    await this.getFormsInfoByBundleName(bundleName, this.setAtomicServiceAppItemFormInfo.bind(this));
368  }
369
370  /**
371   * 从缓存中获取原服务app 卡片信息
372   *
373   * @param bundleName bundleName
374   * @returns CardItemInfo[]
375   */
376  getAtomicServiceAppItemFormInfo(bundleName: string): CardItemInfo[] | undefined {
377    Log.showInfo(TAG, `getAtomicServiceAppItemFormInfo bundleName: ${bundleName}, ` +
378      `appItemFormInfo: ${JSON.stringify(this.mAtomicServiceAppItemFormInfoMap.get(bundleName))}`);
379    return this.mAtomicServiceAppItemFormInfoMap.get(bundleName);
380  }
381
382  /**
383   * 删除原服务卡片信息
384   *
385   * @param bundleName 包名
386   */
387  deleteAtomicServiceAppItemFormInfo(bundleName: string): void {
388    this.mAtomicServiceAppItemFormInfoMap.delete(bundleName);
389  }
390
391  getRealForm(cardItemInfos: CardItemInfo[]): CardItemInfo[] {
392    if (cardItemInfos.length <= 0) {
393      return null;
394    }
395    let result: CardItemInfo[] = [];
396    for (let j: number = 0; j < cardItemInfos.length; j++) {
397      let dimensions: number[] = cardItemInfos[j].supportDimensions;
398      for (let i: number = 0; i < dimensions.length; i++) {
399        const tempCard = new CardItemInfo();
400        this.copyCardItemInfo(tempCard, cardItemInfos[j]);
401        let tempDimensions: number[] = [];
402        tempDimensions.push(dimensions[i]);
403        tempCard.supportDimensions = tempDimensions;
404        tempCard.cardDimension = dimensions[i];
405        result.push(tempCard);
406      }
407    }
408    return result;
409  }
410
411  copyCardItemInfo(newCard: CardItemInfo, oldCard: CardItemInfo): void {
412    newCard.description = oldCard.description;
413    newCard.bundleName = oldCard.bundleName;
414    newCard.abilityName = oldCard.abilityName;
415    newCard.moduleName = oldCard.moduleName;
416    newCard.cardName = oldCard.cardName;
417    newCard.area = oldCard.area;
418    newCard.formConfigAbility = oldCard.formConfigAbility;
419  }
420
421  async getFullFormsInfoByBundleName(bundleName: string, callback?): Promise<CardItemInfo[]> {
422    let currentBundleFormsInfo: CardItemInfo[] = [];
423    currentBundleFormsInfo = await this.getFormsInfoByBundleName(bundleName, callback);
424    currentBundleFormsInfo = this.getRealForm(currentBundleFormsInfo);
425    AppStorage.setOrCreate('formMgrItem', currentBundleFormsInfo);
426    return currentBundleFormsInfo;
427  }
428
429  async doBeforeJumpToFormManager(formBundleName : string): Promise<void> {
430    const formItem = await this.getFullFormsInfoByBundleName(formBundleName);
431    AppStorage.setOrCreate('formItem', formItem);
432  }
433}