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