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}