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