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 = RecentsStyleConstants.DEFAULT_APP_IMAGE; 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 && JSON.stringify(this.recentImage) != JSON.stringify(RecentsStyleConstants.DEFAULT_APP_IMAGE)) { 75 this.recentImage.release().catch((err) => { 76 Log.showError(TAG, `image.PixelMap release err: ${err}`); 77 }) 78 Log.showInfo(TAG, `aboutToDisappear end ${this.missionId}`); 79 } 80 } 81 82 /** 83 * The callback of Recent missions snapshot. 84 * 85 * @param {number} missionId 86 * @param {any} snapShotInfo 87 */ 88 recentMissionsSnapshotCallback(missionId: number, snapShotInfo: SnapShotInfo): void { 89 Log.showDebug(TAG, `recentMissionsSnapshotCallback missionId: ${this.missionId}`); 90 if (!this.mReleaseFlag && missionId === this.missionId) { 91 this.recentImage = snapShotInfo.snapShotImage; 92 let width = snapShotInfo.snapShotImageWidth; 93 let height = snapShotInfo.snapShotImageHeight; 94 Log.showDebug(TAG, `recentMissionsSnapshotCallback recentImage: ${JSON.stringify(this.recentImage)}, 95 width: ${JSON.stringify(width)}, height: ${JSON.stringify(height)}`); 96 } else { 97 snapShotInfo.snapShotImage.release().catch((err) => { 98 Log.showError(TAG, `image.PixelMap release err :${err}`) 99 }) 100 } 101 } 102 103 build() { 104 Column() { 105 Row() { 106 Row() { 107 RecentMissionAppIcon({ 108 iconSize: this.mIsSingleLayout ? 109 RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE * RecentsStyleConstants.DPI_RATIO : 110 RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE, 111 appIcon: this.appIconId, 112 bundleName: this.bundleName, 113 moduleName: this.moduleName, 114 labelId: this.appLabelId, 115 useCache: false 116 }) 117 RecentMissionAppName({ 118 appName: this.appName, 119 nameSize: this.mIsSingleLayout ? 120 RecentsStyleConstants.DEFAULT_APP_NAME_SIZE * RecentsStyleConstants.DPI_RATIO : 121 RecentsStyleConstants.DEFAULT_APP_NAME_SIZE, 122 bundleName: this.bundleName, 123 moduleName: this.moduleName, 124 labelId: this.appLabelId, 125 nameMargin: this.cardMargin / 2 126 }) 127 } 128 .layoutWeight(this.cardLayoutWeight) 129 .margin({ left: RecentsStyleConstants.SINGLE_LIST_APP_INFO_LEFT_MARGIN }) 130 131 .onClick((event) => { 132 this.setStartAppInfo(event?.target?.area?.globalPosition); 133 }) 134 Column() { 135 Image(RecentsStyleConstants.DEFAULT_LOCKED_IMAGE) 136 .visibility(this.lockedState ? Visibility.Visible : Visibility.Hidden) 137 .width(this.mIsSingleLayout ? 138 RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE_NEW : 139 RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE - 10) 140 .height(this.mIsSingleLayout ? 141 RecentsStyleConstants.SINGLE_LIST_DEFAULT_APP_ICON_SIZE_NEW : 142 RecentsStyleConstants.DOUBLE_LIST_DEFAULT_APP_ICON_SIZE - 10) 143 .margin({ 144 right: this.mIsSingleLayout ? (RecentsStyleConstants.SINGLE_LIST_LOCKED_IMAGE_RIGHT_MARGIN) : (RecentsStyleConstants.DOUBLE_LIST_LOCKED_IMAGE_RIGHT_MARGIN) 145 }) 146 .onClick((event) => { 147 this.isClickSubComponent = true; 148 Log.showDebug(TAG, `click set recent mission: ${this.missionId} Locked status: ${!this.lockedState}`); 149 this.mRecentMissionsViewModel.setRecentMissionLock(this.missionId,!this.lockedState); 150 this.setStartAppInfo(event?.target?.area?.globalPosition); 151 this.isClickSubComponent = false; 152 }) 153 } 154 } 155 .margin({ 156 top: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_INFO_TOP_MARGIN : RecentsStyleConstants.DOUBLE_LIST_APP_INFO_BOTTOM_MARGIN, 157 bottom: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_INFO_BOTTOM_MARGIN : RecentsStyleConstants.DOUBLE_LIST_APP_INFO_BOTTOM_MARGIN 158 }) 159 160 Image(this.recentImage) 161 .alt(RecentsStyleConstants.DEFAULT_APP_IMAGE) 162 .objectFit(ImageFit.Fill) 163 .borderRadius(RecentsStyleConstants.RECENT_IMAGE_RADIUS) 164 .width(this.mIsSingleLayout ? 165 RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH : 166 RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH) 167 .height(this.mIsSingleLayout ? 168 RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_HEIGHT : 169 RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_HEIGHT) 170 .onClick((event) => { 171 this.isClickSubComponent = true; 172 Log.showDebug(TAG, 'onClick start launcher ability'); 173 if (!globalThis.recentMode || !windowManager.isSplitWindowMode(globalThis.recentMode)) { 174 windowManager.hideWindow(windowManager.RECENT_WINDOW_NAME); 175 } 176 Trace.start(Trace.CORE_METHOD_START_APP_ANIMATION); 177 this.setStartAppInfo(event?.target?.area?.globalPosition); 178 this.mRecentMissionsViewModel.moveMissionToFront(this.missionId); 179 }) 180 } 181 .width(this.mIsSingleLayout ? 182 RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH : 183 RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH) 184 .height(this.mIsSingleLayout ? 185 RecentsStyleConstants.SINGLE_LIST_MISSION_HEIGHT : 186 RecentsStyleConstants.DOUBLE_LIST_MISSION_HEIGHT) 187 .backgroundColor(RecentsStyleConstants.DEFAULT_BG_COLOR) 188 .gesture( 189 PanGesture({ fingers: 1, direction: PanDirection.Vertical, distance: 5 }) 190 .onActionEnd((e) => { 191 let offsetWidth = (this.mIsSingleLayout ? 192 RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH * RecentsStyleConstants.DPI_RATIO : 193 RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH) / 2; 194 if (e.offsetY < -50 && e.offsetX <= offsetWidth && -offsetWidth <= e.offsetX) { 195 this.mRecentMissionsViewModel.deleteRecentMission(false, this.missionId); 196 } else if (e.offsetY > 50 && e.offsetX <= offsetWidth && -offsetWidth <= e.offsetX) { 197 Log.showDebug(TAG, `gesture set recent mission: ${this.missionId} Locked status: ${!this.lockedState}`); 198 this.mRecentMissionsViewModel.setRecentMissionLock(this.missionId,!this.lockedState); 199 } 200 })) 201 } 202 203 /** 204 * set start app info 205 */ 206 private setStartAppInfo(position) { 207 AppStorage.SetOrCreate('startAppTypeFromPageDesktop', CommonConstants.OVERLAY_TYPE_APP_ICON); 208 let appParams = { 209 bundleName: this.bundleName, 210 moduleName: this.moduleName, 211 appIconId: this.appIconId, 212 borderRadius: RecentsStyleConstants.RECENT_IMAGE_RADIUS, 213 isSingleLayout: this.mIsSingleLayout, 214 appIconSize:this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_APP_IMAGE_WIDTH : RecentsStyleConstants.DOUBLE_LIST_APP_IMAGE_WIDTH, 215 appIconHeight: this.mIsSingleLayout ? RecentsStyleConstants.SINGLE_LIST_MISSION_HEIGHT : RecentsStyleConstants.DOUBLE_LIST_MISSION_HEIGHT, 216 position:position 217 } 218 AppStorage.SetOrCreate('startAppItemInfo', appParams); 219 this.mRecentMissionStartAppHandler.setAppIconSize(appParams.appIconSize, appParams.appIconHeight); 220 this.mRecentMissionStartAppHandler.setAppIconInfo(); 221 } 222}