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