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 image from '@ohos.multimedia.image';
17import missionManager from '@ohos.app.ability.missionManager';
18import { Log } from '../utils/Log';
19import { CheckEmptyUtils } from '../utils/CheckEmptyUtils';
20import { launcherAbilityManager } from './LauncherAbilityManager';
21import { SnapShotInfo } from '../bean/SnapShotInfo';
22import { MissionInfo } from '../bean/MissionInfo';
23import { RecentMissionInfo } from '../bean/RecentMissionInfo';
24import { RecentBundleMissionInfo } from '../bean/RecentBundleMissionInfo';
25import { windowManager } from './WindowManager';
26
27const TAG = 'AmsMissionManager';
28
29/**
30 * missionManager for Launcher
31 */
32class AmsMissionManager {
33  private static readonly RECENT_MISSIONS_LIMIT_NUM = 20;
34  private mMissionId: number;
35  private mLockState: boolean;
36
37  static getInstance(): AmsMissionManager {
38    if (globalThis.AmsMissionManagerInstance == null) {
39      globalThis.AmsMissionManagerInstance = new AmsMissionManager();
40      // remove this if toolchain fix requireNApi bug
41      image.toString();
42    }
43    return globalThis.AmsMissionManagerInstance;
44  }
45
46
47  /**
48   * Get origin recent missions
49   *
50   * @return {Array} missions
51   */
52  async getOriginRecentMissionsList(): Promise<Array<missionManager.MissionInfo>> {
53    try {
54      let arrayMissionInfo = new Array<missionManager.MissionInfo>();
55      let res = await missionManager.getMissionInfos('', AmsMissionManager.RECENT_MISSIONS_LIMIT_NUM);
56      if (!CheckEmptyUtils.isEmptyArr(res)) {
57        Log.showDebug(TAG, `getOriginRecentMissionsList res.length: ${res.length}`);
58        arrayMissionInfo = res;
59        return arrayMissionInfo;
60      }
61    } catch (err) {
62      Log.showError(TAG, `getOriginRecentMissionsList fail, err: ${JSON.stringify(err)}`);
63    }
64  }
65
66  /**
67   * Get recent missions list
68   *
69   * @return {Array} missions list
70   */
71  async getRecentMissionsList(): Promise<Array<RecentMissionInfo>> {
72    const recentMissionsList = new Array<RecentMissionInfo>();
73    let missionInfos: Array<missionManager.MissionInfo> = await this.getOriginRecentMissionsList();
74    if (CheckEmptyUtils.isEmptyArr(missionInfos)) {
75      Log.showDebug(TAG, 'getRecentMissionsList Empty');
76      return recentMissionsList;
77    }
78    for (const recentItem of missionInfos) {
79      const recentMissionInfo = new RecentMissionInfo();
80      recentMissionInfo.missionId = recentItem.missionId;
81      recentMissionInfo.bundleName = recentItem.want.bundleName;
82      recentMissionInfo.abilityName = recentItem.want.abilityName;
83      recentMissionInfo.moduleName = recentItem.want.parameters?.moduleName ? String(recentItem.want.parameters?.moduleName) : '';
84      recentMissionInfo.lockedState = recentItem.lockedState;
85      recentMissionInfo.snapShotTime = recentItem.timestamp;
86      const appInfo = await launcherAbilityManager.getAppInfoByBundleName(recentMissionInfo.bundleName, recentMissionInfo.abilityName);
87      if (appInfo == undefined) {
88        continue;
89      }
90      recentMissionInfo.appLabelId = appInfo.appLabelId;
91      recentMissionInfo.appIconId = appInfo.appIconId;
92      recentMissionInfo.appName = appInfo.appName;
93      recentMissionsList.push(recentMissionInfo);
94    }
95    Log.showInfo(TAG, `getRecentMissionsList recentMissionsList length: ${recentMissionsList.length}`);
96    return recentMissionsList;
97  }
98
99  /**
100   * Get recent missions list group by bundleName
101   *
102   * @return {Array} missions list
103   */
104  async getRecentBundleMissionsList(): Promise<Array<RecentBundleMissionInfo>> {
105    const recentMissionsList = new Array<RecentBundleMissionInfo>();
106    let missionInfos: Array<missionManager.MissionInfo> = await this.getOriginRecentMissionsList();
107    if (CheckEmptyUtils.isEmptyArr(missionInfos)) {
108      Log.showDebug(TAG, 'getRecentBundleMissionsList Empty');
109      return recentMissionsList;
110    }
111    for (let i = 0; i < missionInfos.length; i++) {
112      let missionInfo = missionInfos[i];
113      let bundleName = missionInfo.want.bundleName!;
114      let abilityName = missionInfo.want.abilityName!;
115      let localMissionInfo = recentMissionsList.find((item) => item.bundleName === bundleName);
116      if (localMissionInfo) {
117        let missionInfoAdd = new MissionInfo();
118        missionInfoAdd.missionId = missionInfo.missionId;
119        localMissionInfo.missionInfoList!.push(missionInfoAdd);
120      } else {
121        let recentTaskInfo = new RecentBundleMissionInfo();
122        recentTaskInfo.bundleName = bundleName;
123        const appInfo = await launcherAbilityManager.getAppInfoByBundleName(bundleName, abilityName);
124        if (appInfo == undefined) {
125          continue;
126        }
127        recentTaskInfo.abilityName = appInfo.abilityName;
128        recentTaskInfo.moduleName = appInfo.moduleName;
129        recentTaskInfo.keyName = bundleName + appInfo.abilityName + appInfo.moduleName;
130        recentTaskInfo.appLabelId = appInfo.appLabelId;
131        recentTaskInfo.appIconId = appInfo.appIconId;
132        recentTaskInfo.appName = appInfo.appName;
133        recentTaskInfo.missionInfoList = new Array<MissionInfo>();
134        let missionInfoAdd = new MissionInfo();
135        missionInfoAdd.missionId = missionInfo.missionId;
136        recentTaskInfo.missionInfoList.push(missionInfoAdd);
137        recentMissionsList.push(recentTaskInfo);
138      }
139    }
140    Log.showInfo(TAG, `getRecentBundleMissionsList recentMissionsList length:${recentMissionsList.length}`);
141    return recentMissionsList;
142  }
143
144  /**
145   * Clear the given mission in the ability manager service.
146   *
147   * @param missionId
148   */
149  async clearMission(missionId: number): Promise<void> {
150    Log.showInfo(TAG, `clearMission Id:${missionId}`);
151    try {
152      await missionManager.clearMission(missionId);
153      Log.showDebug(TAG, 'clearMission success');
154    } catch (err) {
155      Log.showError(TAG, `clearMission fail, err:${JSON.stringify(err)}`);
156    }
157  }
158
159  /**
160   * Clear all missions in the ability manager service.
161   * locked mission will not clear
162   *
163   * @return nothing.
164   */
165  async clearAllMissions(): Promise<void> {
166    try {
167      await missionManager.clearAllMissions();
168      Log.showDebug(TAG, 'clearAllMissions success');
169    } catch (err) {
170      Log.showError(TAG, `clearAllMissions fail, err: ${JSON.stringify(err)}`);
171    }
172  }
173
174  /**
175   * lockMission
176   *
177   * @param missionId mission id to lock.
178   */
179  lockMission(missionId: number): void {
180    Log.showInfo(TAG, `lockMission start! missionId: ${missionId}`);
181    this.mMissionId = missionId;
182    this.mLockState = true;
183    missionManager.lockMission(missionId, this.lockCallback.bind(this));
184  }
185
186  /**
187   * unlockMission
188   *
189   * @param missionId mission id to unlock.
190   */
191  unlockMission(missionId: number): void {
192    Log.showInfo(TAG, `unlockMission start! missionId: ${missionId}`);
193    this.mMissionId = missionId;
194    this.mLockState = false;
195    missionManager.unlockMission(missionId, this.lockCallback.bind(this));
196  }
197
198  private async lockCallback(): Promise<void> {
199    Log.showDebug(TAG, 'lockCallback start');
200    // update mission recent card
201    let mRecentMissionsList = await amsMissionManager.getRecentMissionsList();
202    mRecentMissionsList.find(item => {
203      return item.missionId === this.mMissionId;
204    }).lockedState = this.mLockState;
205    if (globalThis.recentMode && windowManager.isSplitWindowMode(globalThis.recentMode)) {
206      mRecentMissionsList.forEach((item, index) => {
207        if (item.missionId == globalThis.splitMissionId) {
208          mRecentMissionsList.splice(index, 1);
209          return;
210        }
211      });
212    }
213    AppStorage.setOrCreate('recentMissionsList', mRecentMissionsList);
214  };
215
216  /**
217   * Get recent mission snapshot info
218   *
219   * @param missionId mission id to get snapshot.
220   * @return snapshot info
221   */
222  async getMissionSnapShot(missionId: number): Promise<SnapShotInfo> {
223    let snapShotInfo: SnapShotInfo = new SnapShotInfo();
224    Log.showInfo(TAG, `getMissionSnapShot start! missionId: ${missionId}`);
225    try {
226      let missionSnapshot: missionManager.MissionSnapshot = null;
227      missionSnapshot = await missionManager.getMissionSnapShot('', missionId);
228      Log.showInfo(TAG, 'getMissionSnapShot end');
229      const imageInfo = await missionSnapshot.snapshot.getImageInfo();
230      Log.showDebug(TAG, `getMissionSnapShot ${missionId} success ${JSON.stringify(imageInfo)}`);
231      snapShotInfo.missionId = missionId;
232      snapShotInfo.snapShotImage = missionSnapshot.snapshot;
233      snapShotInfo.snapShotImageWidth = imageInfo.size.width;
234      snapShotInfo.snapShotImageHeight = imageInfo.size.height;
235    } catch (err) {
236      Log.showError(TAG, `missionManager.getMissionSnapShot fail, missionId:${missionId} err: ${JSON.stringify(err)}`);
237    }
238    return snapShotInfo;
239  }
240
241  /**
242   * Move mission to front
243   *
244   * @param missionId
245   */
246  async moveMissionToFront(missionId: number, winMode?: number) {
247    Log.showInfo(TAG, `moveMissionToFront missionId:  ${missionId}`);
248    try {
249      winMode ? await missionManager.moveMissionToFront(missionId, { windowMode: winMode }):
250        await missionManager.moveMissionToFront(missionId);
251      Log.showDebug(TAG, 'moveMissionToFront missionId end success');
252    } catch (err) {
253      Log.showError(TAG, `moveMissionToFront fail, err: ${JSON.stringify(err)}`);
254    }
255  }
256}
257
258export const amsMissionManager = AmsMissionManager.getInstance();