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 */ 15 16import { Log } from '../utils/Log'; 17import { SettingsModel } from '../model/SettingsModel'; 18import { StyleConstants } from '../constants/StyleConstants'; 19import { CommonConstants } from '../constants/CommonConstants'; 20import { ResourceManager } from '../manager/ResourceManager'; 21import { PresetStyleConstants } from '../constants/PresetStyleConstants'; 22import { AppIcon } from './AppIcon'; 23import { AppName } from './AppName'; 24import { AppMenu } from './AppMenu'; 25import { LauncherDragItemInfo } from '../bean/LauncherDragItemInfo'; 26 27const TAG = 'FolderComponent'; 28 29@Component 30export struct FolderComponent { 31 @StorageLink('openFolderStatus') @Watch('updateFolderAnimate') openFolderStatus: number = 1; 32 @State folderAnimateData: { 33 folderId: string, 34 isOpenFolder: boolean, 35 } = { folderId: '', isOpenFolder: false }; 36 @State folderPositionX: number = 0; 37 @State folderPositionY: number = 0; 38 @State folderItemPositionX: number = 0; 39 @State folderItemPositionY: number = 0; 40 @State animateFolderPositionX: number = 0; 41 @State animateFolderPositionY: number = 0; 42 @State animateOpacity: number = 1.0; 43 @State animateScale: number = 1.0; 44 @State showFolderName: boolean = true; 45 @State folderNameHeight: number = 0; 46 @State folderNameSize: number = 0; 47 @State nameFontColor: string = ''; 48 @State appIconSize: number = 0; 49 @State superposeIconVisible: boolean = false; 50 @State isHover: boolean = false; 51 mPaddingTop: number = StyleConstants.DEFAULT_10; 52 folderGridSize: number = StyleConstants.DEFAULT_FOLDER_GRID_SIZE; 53 gridMargin: number = StyleConstants.DEFAULT_FOLDER_GRID_MARGIN; 54 gridGap: number = StyleConstants.DEFAULT_FOLDER_GRID_GAP; 55 badgeNumber: number = 0; 56 private mSettingsModel: SettingsModel; 57 private isPad: boolean = false; 58 private mFolderItem: any; 59 private mShowAppList: any = []; 60 private mSuperposeAppList: any = []; 61 onAppIconClick?: Function = null; 62 onOpenFolderClick?: Function = null; 63 onFolderTouch?: Function = null; 64 onGetPosition?: Function = null; 65 buildMenu: Function = null; 66 folderNameLines: number = PresetStyleConstants.DEFAULT_APP_NAME_LINES; 67 iconNameMargin: number = PresetStyleConstants.DEFAULT_ICON_NAME_GAP; 68 isSelect?: boolean; 69 dragStart: Function; 70 71 aboutToAppear(): void { 72 Log.showInfo(TAG, `aboutToAppear start`); 73 this.updateShowList(); 74 this.mSettingsModel = SettingsModel.getInstance(); 75 if (this.mSettingsModel.getDevice() != "phone") { 76 this.isPad = true; 77 } 78 } 79 80 aboutToDisappear(): void { 81 this.onAppIconClick = null; 82 this.onOpenFolderClick = null; 83 this.onFolderTouch = null; 84 this.onGetPosition = null; 85 this.buildMenu = null; 86 this.dragStart = null; 87 } 88 89 updateShowList(): void { 90 if (this.mFolderItem.layoutInfo[0].length > CommonConstants.FOLDER_STATIC_SHOW_LENGTH) { 91 this.mShowAppList = this.mFolderItem.layoutInfo[0].slice(0, CommonConstants.FOLDER_STATIC_SHOW_LENGTH); 92 } else { 93 this.mShowAppList = this.mFolderItem.layoutInfo[0]; 94 } 95 96 let showLength = CommonConstants.FOLDER_STATIC_SHOW_LENGTH - CommonConstants.FOLDER_STATIC_SUPERPOSEAPP_LENGTH; 97 if (this.mShowAppList.length > showLength) { 98 this.mSuperposeAppList = this.mShowAppList.slice(showLength); 99 this.mShowAppList = this.mShowAppList.slice(0, showLength); 100 this.superposeIconVisible = true; 101 } 102 103 let length = this.mSuperposeAppList.length; 104 if (length > CommonConstants.FOLDER_STATIC_SUPERPOSEAPP_LENGTH) { 105 this.mSuperposeAppList = this.mSuperposeAppList.slice(0, CommonConstants.FOLDER_STATIC_SUPERPOSEAPP_LENGTH); 106 } else { 107 for (let i = 0; i < (CommonConstants.FOLDER_STATIC_SUPERPOSEAPP_LENGTH - length); i++) { 108 this.mSuperposeAppList.push({ 'isEmpty': true }); 109 } 110 } 111 this.mSuperposeAppList = this.mSuperposeAppList.reverse(); 112 this.mSuperposeAppList[0].alignContent = Alignment.TopStart; 113 this.mSuperposeAppList[1].alignContent = Alignment.Center; 114 this.mSuperposeAppList[2].alignContent = Alignment.BottomEnd; 115 116 Log.showInfo(TAG, `superposeIconVisible:${this.superposeIconVisible}`); 117 Log.showInfo(TAG, `FolderItem.layoutInfo[0].length:${this.mFolderItem.layoutInfo[0].length}`); 118 Log.showInfo(TAG, `SuperposeAppList length:${this.mSuperposeAppList.length}`); 119 } 120 121 @Builder MenuBuilder() { 122 Column() { 123 AppMenu({ 124 menuInfoList: this.buildMenu(this.mFolderItem), 125 }) 126 } 127 .alignItems(HorizontalAlign.Center) 128 .justifyContent(FlexAlign.Center) 129 .width(StyleConstants.CONTEXT_MENU_WIDTH) 130 } 131 132 private updateFolderAnimate() { 133 Log.showInfo(TAG, `updateFolderAnimate start`); 134 if (this.openFolderStatus == 0) { 135 this.folderAnimateData = AppStorage.get('folderAnimateData'); 136 if (this.mFolderItem.folderId === this.folderAnimateData.folderId && 137 this.folderAnimateData.isOpenFolder && 138 this.folderAnimateData.folderId != '' && 139 this.animateOpacity != 1.0 && 140 this.animateScale != 1.0) { 141 this.folderAnimateData.isOpenFolder = false; 142 AppStorage.setOrCreate('folderAnimateData', this.folderAnimateData); 143 Log.showInfo(TAG, `updateFolderAnimate show`); 144 this.showAnimate(1.0, 1.0, false); 145 } 146 } 147 } 148 149 private showAnimate(animateScale: number, animateOpacity: number, isMoveFolder: boolean) { 150 let positionX = 0; 151 let positionY = 0; 152 if (this.onGetPosition) { 153 this.onGetPosition(this.getPosition.bind(this)); 154 if (isMoveFolder) { 155 positionX = this.animateFolderPositionX; 156 positionY = this.animateFolderPositionY; 157 } 158 } 159 animateTo({ 160 duration: 250, 161 tempo: 0.5, 162 curve: Curve.Friction, 163 delay: 0, 164 iterations: 1, 165 playMode: PlayMode.Normal, 166 onFinish: () => { 167 Log.showInfo(TAG, ` onFinish x: ${this.folderPositionX}, y: ${this.folderPositionY}`); 168 } 169 }, () => { 170 this.animateScale = animateScale; 171 this.animateOpacity = animateOpacity; 172 this.folderPositionX = positionX; 173 this.folderPositionY = positionY; 174 }) 175 } 176 177 public getPosition(x: number, y: number): void { 178 this.folderItemPositionX = x; 179 this.folderItemPositionY = y; 180 let screenWidth: number = AppStorage.get('screenWidth'); 181 let screenHeight: number = AppStorage.get('screenHeight'); 182 this.animateFolderPositionX = (screenWidth - this.folderGridSize * 1.5) / 2 - this.folderItemPositionX; 183 this.animateFolderPositionY = (screenHeight - this.folderGridSize * 1.5) / 2 - this.folderItemPositionY; 184 Log.showInfo(TAG, `getPosition animatePosition x: ${this.animateFolderPositionX}, y: ${this.animateFolderPositionY}`); 185 } 186 187 build() { 188 Column() { 189 Column() { 190 Badge({ 191 count: this.badgeNumber, 192 maxCount: StyleConstants.MAX_BADGE_COUNT, 193 style: { 194 color: StyleConstants.DEFAULT_FONT_COLOR, 195 fontSize: StyleConstants.DEFAULT_BADGE_FONT_SIZE, 196 badgeSize: (this.badgeNumber > 0 ? StyleConstants.DEFAULT_BADGE_SIZE : 0), 197 badgeColor: Color.Red, 198 } 199 }) { 200 Stack() { 201 Column() { 202 } 203 .backgroundColor(Color.White) 204 .borderRadius(24) 205 .opacity(0.5) 206 .height(this.folderGridSize) 207 .width(this.folderGridSize) 208 209 Grid() { 210 ForEach(this.mShowAppList, (item) => { 211 GridItem() { 212 AppIcon({ 213 iconSize: this.appIconSize, 214 iconId: item.appIconId, 215 icon: ResourceManager.getInstance().getCachedAppIcon(item.appIconId, item.bundleName, item.moduleName), 216 bundleName: item.bundleName, 217 moduleName: item.moduleName, 218 badgeNumber: item.badgeNumber 219 }) 220 } 221 .height(StyleConstants.PERCENTAGE_100) 222 .width(StyleConstants.PERCENTAGE_100) 223 .onClick((event: ClickEvent) => { 224 if (this.onAppIconClick) { 225 this.onAppIconClick(event, item); 226 } 227 }) 228 }, (item) => JSON.stringify(item)) 229 230 if (this.mSuperposeAppList.length > 0) { 231 GridItem() { 232 Stack() { 233 ForEach(this.mSuperposeAppList, (item) => { 234 Stack({ alignContent: item.alignContent }) { 235 if (item.isEmpty) { 236 Column() { 237 Column() { 238 } 239 .backgroundColor(Color.White) 240 .borderRadius(10) 241 .opacity(0.5) 242 .width(StyleConstants.PERCENTAGE_100) 243 .height(StyleConstants.PERCENTAGE_100) 244 } 245 .alignItems(HorizontalAlign.Start) 246 .width(StyleConstants.PERCENTAGE_80) 247 .height(StyleConstants.PERCENTAGE_80) 248 } else { 249 Column() { 250 AppIcon({ 251 iconSize: this.appIconSize * StyleConstants.PERCENTAGE_80_number, 252 iconId: item.appIconId, 253 icon: ResourceManager.getInstance().getCachedAppIcon(item.appIconId, item.bundleName, item.moduleName), 254 bundleName: item.bundleName, 255 moduleName: item.moduleName, 256 badgeNumber: item.badgeNumber 257 }) 258 } 259 .width(StyleConstants.PERCENTAGE_80) 260 .height(StyleConstants.PERCENTAGE_80) 261 .alignItems(HorizontalAlign.Start) 262 } 263 } 264 .width(StyleConstants.PERCENTAGE_100) 265 .height(StyleConstants.PERCENTAGE_100) 266 }, (item) => JSON.stringify(item)) 267 } 268 .width(this.isPad ? 269 StyleConstants.DEFAULT_FOLDER_APP_ITEM_WIDTH_SMALL : 270 StyleConstants.DEFAULT_FOLDER_APP_ITEM_WIDTH) 271 .height(this.isPad ? 272 StyleConstants.DEFAULT_FOLDER_APP_ITEM_WIDTH_SMALL : 273 StyleConstants.DEFAULT_FOLDER_APP_ITEM_WIDTH) 274 } 275 .visibility(this.superposeIconVisible ? Visibility.Visible : Visibility.Hidden) 276 .width(StyleConstants.PERCENTAGE_100) 277 .height(StyleConstants.PERCENTAGE_100) 278 .onClick((event: ClickEvent) => { 279 Log.showInfo(TAG, `last item onClick`); 280 this.showAnimate(1.5, 0, true); 281 if (this.onOpenFolderClick) { 282 this.folderAnimateData.folderId = this.mFolderItem.folderId; 283 this.folderAnimateData.isOpenFolder = true; 284 AppStorage.setOrCreate('folderAnimateData', this.folderAnimateData); 285 this.onOpenFolderClick(event, this.mFolderItem); 286 } 287 }) 288 } 289 } 290 .padding(this.gridMargin) 291 .columnsTemplate('1fr 1fr 1fr') 292 .rowsTemplate('1fr 1fr 1fr') 293 .columnsGap(this.gridGap) 294 .rowsGap(this.gridGap) 295 .onClick((event: ClickEvent) => { 296 Log.showInfo(TAG, `grid onClick`); 297 this.showAnimate(1.5, 0, true); 298 if (this.onOpenFolderClick) { 299 this.folderAnimateData.folderId = this.mFolderItem.folderId; 300 this.folderAnimateData.isOpenFolder = true; 301 AppStorage.setOrCreate('folderAnimateData', this.folderAnimateData); 302 this.onOpenFolderClick(event, this.mFolderItem); 303 } 304 }) 305 .onTouch((event: TouchEvent) => { 306 Log.showInfo(TAG, "onTouch start"); 307 if (this.onFolderTouch) { 308 this.onFolderTouch(event, this.mFolderItem); 309 } 310 Log.showInfo(TAG, "onTouch end"); 311 }) 312 } 313 .height(StyleConstants.PERCENTAGE_100) 314 .width(StyleConstants.PERCENTAGE_100) 315 .onHover((isHover: boolean) => { 316 Log.showInfo(TAG, `onHover isHover:${isHover}`); 317 this.isHover = isHover; 318 }) 319 .bindContextMenu(this.MenuBuilder, ResponseType.LongPress) 320 .onDragStart((event: DragEvent) => { 321 return this.dragStart(event); 322 }) 323 .onDragEnd((event: DragEvent, extraParams: string) => { 324 Log.showInfo(TAG, `onDragEnd event: [${event.getWindowX()}, ${event.getWindowY()}]` + event.getResult()); 325 AppStorage.setOrCreate<LauncherDragItemInfo>('dragItemInfo', new LauncherDragItemInfo()); 326 }) 327 } 328 .height(this.folderGridSize) 329 .width(this.folderGridSize) 330 331 Column() { 332 AppName({ 333 nameHeight: this.folderNameHeight, 334 nameSize: this.folderNameSize, 335 nameFontColor: this.nameFontColor, 336 appName: this.mFolderItem.folderName, 337 nameLines: this.folderNameLines, 338 marginTop: this.iconNameMargin 339 }) 340 } 341 .visibility(this.showFolderName ? Visibility.Visible : Visibility.Hidden) 342 } 343 .bindContextMenu(this.MenuBuilder, ResponseType.RightClick) 344 .width(StyleConstants.PERCENTAGE_100) 345 .height(StyleConstants.PERCENTAGE_100) 346 .offset({ x: this.folderPositionX, y: this.folderPositionY }) 347 .scale({ x: this.isHover ? 1.05 : this.animateScale, y: this.isHover ? 1.05 : this.animateScale }) 348 .opacity(this.animateOpacity) 349 } 350 .width(this.isSelect ? this.folderGridSize + StyleConstants.DEFAULT_40 : StyleConstants.PERCENTAGE_100) 351 .height(this.isSelect ? this.folderGridSize + StyleConstants.DEFAULT_40 : StyleConstants.PERCENTAGE_100) 352 .backgroundColor(this.isSelect ? StyleConstants.DEFAULT_BROAD_COLOR : StyleConstants.DEFAULT_TRANSPARENT_COLOR) 353 .borderRadius(this.isSelect ? StyleConstants.DEFAULT_15 : StyleConstants.DEFAULT_0) 354 .padding(this.isSelect ? { left: StyleConstants.DEFAULT_20, 355 right: StyleConstants.DEFAULT_20, top: this.mPaddingTop + StyleConstants.DEFAULT_10 } : { top: this.mPaddingTop }) 356 } 357}