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