• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
15import image from '@ohos.multimedia.image';
16import {
17  EventConstants,
18  localEventManager,
19  Log,
20  Trace,
21  CheckEmptyUtils,
22  windowManager,
23  SnapShotInfo,
24  CommonConstants,
25  LauncherDragItemInfo
26} from '@ohos/common';
27import RecentMissionAppIcon from './RecentMissionAppIcon';
28import RecentMissionAppName from './RecentMissionAppName';
29import { RecentMissionsViewModel } from '../../viewmodel/RecentMissionsViewModel';
30import { RecentsStyleConstants } from '../../common/constants/RecentsStyleConstants';
31import { RecentMissionStartAppHandler } from './../RecentMissionStartAppHandler';
32import { BusinessError } from '@ohos.base';
33
34const TAG = 'Recent-RecentMissionCard';
35
36interface AppParams {
37  bundleName: string;
38  moduleName: string;
39  appIconId: number;
40  borderRadius?: number;
41  isSingleLayout?: boolean;
42  appIconSize?: number;
43  appIconHeight?: number;
44  position?: Position
45}
46
47/**
48 * Common card component for recent missions.
49 */
50@Component
51export default struct RecentMissionCard {
52  @Link isClickSubComponent: boolean;
53  @State missionId: number = 0;
54  @State appIconId: number = 0;
55  @State appLabelId: number = 0;
56  @State appName: string = '';
57  @State bundleName: string = '';
58  @State moduleName: string = '';
59  @State abilityName: string = '';
60  @State lockedState: boolean = false;
61  @State cardMargin: number = RecentsStyleConstants.SINGLE_LIST_LAYOUT_MARGIN;
62  @State cardLayoutWeight: number = RecentsStyleConstants.SINGLE_LIST_APP_INFO_LAYOUT_WEIGHT;
63  // recentImage default is Resource type, to avoid toolchain typeCheck
64  @State recentImage?: image.PixelMap = undefined;
65  private mIsSingleLayout: boolean = true;
66  private snapShotTime: string = '';
67  private mRecentMissionsViewModel?: RecentMissionsViewModel;
68  private mRecentMissionStartAppHandler?: RecentMissionStartAppHandler;
69  private mReleaseFlag = true;
70
71  aboutToAppear(): void {
72    Log.showInfo(TAG, 'aboutToAppear start');
73    // remove this if toolchain fix requireNApi bug
74    this.mReleaseFlag = false;
75    this.mRecentMissionsViewModel = RecentMissionsViewModel.getInstance();
76    this.mIsSingleLayout = this.mRecentMissionsViewModel.getRecentMissionsRowType() === 'single' ? true : false;
77    this.cardMargin = this.mRecentMissionsViewModel.getRecentMissionsRowType() === 'single' ?
78      RecentsStyleConstants.SINGLE_LIST_LAYOUT_MARGIN * RecentsStyleConstants.DPI_RATIO : RecentsStyleConstants.DOUBLE_LIST_LAYOUT_MARGIN;
79    this.cardLayoutWeight = this.mRecentMissionsViewModel.getRecentMissionsRowType() === 'single' ?
80      RecentsStyleConstants.SINGLE_LIST_APP_INFO_LAYOUT_WEIGHT : RecentsStyleConstants.DOUBLE_LIST_APP_INFO_LAYOUT_WEIGHT;
81    this.mRecentMissionsViewModel.getMissionSnapShot(this.missionId, this.recentMissionsSnapshotCallback);
82    this.mRecentMissionStartAppHandler = RecentMissionStartAppHandler.getInstance();
83  }
84
85  aboutToDisappear(): void {
86    Log.showInfo(TAG, `aboutToDisappear start ${this.missionId}`);
87    Log.showDebug(TAG, `typeof this.recentImage ${typeof this.recentImage}`);
88    this.mReleaseFlag = true;
89    // typeof Resource or pixelMap is Object
90    if (!CheckEmptyUtils.isEmpty(this.recentImage)) {
91      this.recentImage?.release().catch((err: BusinessError) => {
92        Log.showError(TAG, `image.PixelMap release err: ${JSON.stringify(err)}`);
93      })
94      Log.showInfo(TAG, `aboutToDisappear end ${this.missionId}`);
95    }
96  }
97
98  /**
99 * The callback of Recent missions snapshot.
100 *
101 * @param {number} missionId
102 * @param {any} snapShotInfo
103 */
104  recentMissionsSnapshotCallback = (missionId: number, snapShotInfo: SnapShotInfo): void => {
105    Log.showDebug(TAG, `recentMissionsSnapshotCallback missionId: ${this.missionId}`);
106    if (!this.mReleaseFlag && missionId === this.missionId) {
107      this.recentImage = snapShotInfo.snapShotImage;
108      let width = snapShotInfo.snapShotImageWidth;
109      let height = snapShotInfo.snapShotImageHeight;
110      Log.showDebug(TAG, `recentMissionsSnapshotCallback recentImage: ${JSON.stringify(this.recentImage)},
111        width: ${JSON.stringify(width)}, height: ${JSON.stringify(height)}`);
112    } else {
113      snapShotInfo.snapShotImage.release().catch((err: BusinessError) => {
114        Log.showError(TAG, `image.PixelMap release err :${JSON.stringify(err)}`)
115      })
116    }
117  }
118
119  build() {
120    Column() {
121      Row() {
122        Row() {
123          RecentMissionAppIcon({
124            iconSize: this.mIsSingleLayout ?
125              RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE * RecentsStyleConstants.DPI_RATIO :
126              RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE,
127            appIcon: this.appIconId,
128            bundleName: this.bundleName,
129            moduleName: this.moduleName,
130            labelId: this.appLabelId,
131            useCache: false
132          })
133          RecentMissionAppName({
134            appName: this.appName,
135            nameSize: this.mIsSingleLayout ?
136              RecentsStyleConstants.DEFAULT_APP_NAME_SIZE * RecentsStyleConstants.DPI_RATIO :
137              RecentsStyleConstants.DEFAULT_APP_NAME_SIZE,
138            bundleName: this.bundleName,
139            moduleName: this.moduleName,
140            labelId: this.appLabelId,
141            nameMargin: this.cardMargin / 2
142          })
143        }
144        .layoutWeight(this.cardLayoutWeight)
145        .margin({ left: RecentsStyleConstants.SINGLE_LIST_APP_INFO_LEFT_MARGIN })
146
147        .onClick((event) => {
148          this.setStartAppInfo(event?.target?.area?.globalPosition);
149        })
150        if (this.lockedState) {
151          Column() {
152            Image(RecentsStyleConstants.DEFAULT_LOCKED_IMAGE)
153              .width(this.mIsSingleLayout ?
154              RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE_NEW :
155                RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE - 10)
156              .height(this.mIsSingleLayout ?
157              RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE_NEW :
158                RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE - 10)
159              .margin({
160                right: this.mIsSingleLayout ? (RecentsStyleConstants.SINGLE_LIST_LOCKED_IMAGE_RIGHT_MARGIN) : (RecentsStyleConstants.DOUBLE_LIST_LOCKED_IMAGE_RIGHT_MARGIN)
161              })
162              .onClick((event) => {
163                this.isClickSubComponent = true;
164                Log.showDebug(TAG, `click set recent mission: ${this.missionId} Locked status: ${!this.lockedState}`);
165                RecentMissionsViewModel.getInstance().setRecentMissionLock(this.missionId,!this.lockedState);
166                this.setStartAppInfo(event?.target?.area?.globalPosition);
167                this.isClickSubComponent = false;
168              })
169          }
170        }
171      }
172      .margin({
173        top: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_INFO_TOP_MARGIN : RecentsStyleConstants.DOUBLE_LIST_APP_INFO_BOTTOM_MARGIN,
174        bottom: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_INFO_BOTTOM_MARGIN : RecentsStyleConstants.DOUBLE_LIST_APP_INFO_BOTTOM_MARGIN
175      })
176
177      Image(this.recentImage)
178        .objectFit(ImageFit.Fill)
179        .borderRadius(RecentsStyleConstants.RECENT_IMAGE_RADIUS)
180        .width(this.mIsSingleLayout ?
181          RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH :
182          RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH)
183        .height(this.mIsSingleLayout ?
184          RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_HEIGHT :
185          RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_HEIGHT)
186        .onClick((event) => {
187          this.isClickSubComponent = true;
188          Log.showDebug(TAG, 'onClick start launcher ability');
189          localEventManager.sendLocalEventSticky(EventConstants.EVENT_OPEN_FOLDER_TO_CLOSE, null);
190          windowManager.destroyWindow(windowManager.FORM_MANAGER_WINDOW_NAME);
191          windowManager.destroyWindow(windowManager.FORM_SERVICE_WINDOW_NAME);
192          if (!windowManager.recentMode || !windowManager.isSplitWindowMode(windowManager.recentMode)) {
193            windowManager.hideWindow(windowManager.RECENT_WINDOW_NAME);
194          }
195          Trace.start(Trace.CORE_METHOD_START_APP_ANIMATION);
196          this.setStartAppInfo(event?.target?.area?.globalPosition);
197          RecentMissionsViewModel.getInstance().moveMissionToFront(this.missionId);
198        })
199    }
200    .width(this.mIsSingleLayout ?
201      RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH :
202      RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH)
203    .height(this.mIsSingleLayout ?
204      RecentsStyleConstants.SINGLE_LIST_MISSION_HEIGHT :
205      RecentsStyleConstants.DOUBLE_LIST_MISSION_HEIGHT)
206    .backgroundColor(RecentsStyleConstants.DEFAULT_BG_COLOR)
207    .gesture(
208    PanGesture({ fingers: 1, direction: PanDirection.Vertical, distance: 5 })
209      .onActionEnd((e) => {
210        let offsetWidth = (this.mIsSingleLayout ?
211          RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH * RecentsStyleConstants.DPI_RATIO :
212          RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH) / 2;
213        if (e.offsetY < -50 && e.offsetX <= offsetWidth && -offsetWidth <= e.offsetX) {
214          RecentMissionsViewModel.getInstance().deleteRecentMission(false, this.missionId);
215        } else if (e.offsetY > 50 && e.offsetX <= offsetWidth && -offsetWidth <= e.offsetX) {
216          Log.showDebug(TAG, `gesture set recent mission: ${this.missionId} Locked status: ${!this.lockedState}`);
217          RecentMissionsViewModel.getInstance().setRecentMissionLock(this.missionId,!this.lockedState);
218        }
219      }))
220  }
221
222  /**
223   * set start app info
224   */
225  private setStartAppInfo(position?: Position) {
226    AppStorage.setOrCreate('startAppTypeFromPageDesktop', CommonConstants.OVERLAY_TYPE_APP_ICON);
227    let appParams: AppParams = {
228      bundleName: this.bundleName,
229      moduleName: this.moduleName,
230      appIconId: this.appIconId,
231      borderRadius: RecentsStyleConstants.RECENT_IMAGE_RADIUS,
232      isSingleLayout: this.mIsSingleLayout,
233      appIconSize:this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH : RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH,
234      appIconHeight: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_MISSION_HEIGHT : RecentsStyleConstants.DOUBLE_LIST_MISSION_HEIGHT,
235      position: position
236    }
237    AppStorage.setOrCreate('startAppItemInfo', appParams);
238    this.mRecentMissionStartAppHandler?.setAppIconSize(appParams.appIconSize, appParams.appIconHeight);
239    this.mRecentMissionStartAppHandler?.setAppIconInfo();
240  }
241}