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