• 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 {
17  Log,
18  BaseDragHandler,
19  CommonConstants,
20  CheckEmptyUtils,
21  SettingsModel,
22  EventConstants,
23  localEventManager,
24  layoutConfigManager,
25  PageDesktopModel
26} from '@ohos/common';
27import type {
28  LauncherDragItemInfo,
29  DragArea,
30  DragItemPosition
31} from '@ohos/common';
32import { FormViewModel } from '@ohos/form';
33import { BigFolderViewModel } from '@ohos/bigfolder';
34import { PageDesktopGridStyleConfig } from './PageDesktopGridStyleConfig';
35import PageDesktopConstants from './constants/PageDesktopConstants'
36
37const TAG = 'PageDesktopDragHandler';
38
39/**
40 * Desktop workspace drag and drop processing class
41 */
42export class PageDesktopDragHandler extends BaseDragHandler {
43  private readonly mPageDesktopStyleConfig: PageDesktopGridStyleConfig;
44  private readonly mBigFolderViewModel: BigFolderViewModel;
45  private readonly mFormViewModel: FormViewModel;
46  private readonly mSettingsModel: SettingsModel;
47  private readonly mPageDesktopModel: PageDesktopModel;
48  private mGridConfig;
49  private mPageCoordinateData = {
50    gridXAxis: [],
51    gridYAxis: []
52  };
53  mStartPosition: DragItemPosition;
54  private mEndPosition: DragItemPosition;
55  private readonly styleConfig;
56  private mGridItemHeight: number;
57  private mGridItemWidth: number;
58
59  private constructor() {
60    super();
61    this.mBigFolderViewModel = BigFolderViewModel.getInstance();
62    this.mSettingsModel = SettingsModel.getInstance();
63    this.mFormViewModel = FormViewModel.getInstance();
64    this.mPageDesktopModel = PageDesktopModel.getInstance();
65    this.mPageDesktopStyleConfig = layoutConfigManager.getStyleConfig(PageDesktopGridStyleConfig.APP_GRID_STYLE_CONFIG,
66      PageDesktopConstants.FEATURE_NAME);
67  }
68
69  static getInstance(): PageDesktopDragHandler {
70    if (globalThis.PageDesktopDragHandler == null) {
71      globalThis.PageDesktopDragHandler = new PageDesktopDragHandler();
72    }
73    return globalThis.PageDesktopDragHandler;
74  }
75
76  setDragEffectArea(effectArea): void {
77    Log.showDebug(TAG, `setDragEffectArea:${JSON.stringify(effectArea)}`)
78    AppStorage.setOrCreate('pageDesktopDragEffectArea', effectArea);
79    super.setDragEffectArea(effectArea);
80    this.updateGridParam(effectArea);
81  }
82
83  isDragEffectArea(x: number, y: number): boolean {
84    const isInEffectArea = super.isDragEffectArea(x, y);
85    Log.showDebug(TAG, `isDragEffectArea x: ${x}, y: ${y}, isInEffectArea: ${isInEffectArea}`);
86    const deviceType: string = AppStorage.get('deviceType');
87    const smartDockDragEffectArea: DragArea = AppStorage.get('smartDockDragEffectArea');
88    Log.showDebug(TAG, `isDragEffectArea smartDockDragEffectArea: ${JSON.stringify(smartDockDragEffectArea)}`);
89    if (smartDockDragEffectArea) {
90      if (deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
91        if (isInEffectArea || (y <= smartDockDragEffectArea.bottom && y >= smartDockDragEffectArea.top)
92        && x <= smartDockDragEffectArea.right && x >= smartDockDragEffectArea.left) {
93          return true;
94        }
95        return false;
96      }
97      return isInEffectArea;
98    }
99    return false;
100  }
101
102  getRow(index: number): number {
103    return ~~(index / this.mSettingsModel.getGridConfig().column);
104  }
105
106  getColumn(index: number): number {
107    return index % this.mSettingsModel.getGridConfig().column;
108  }
109
110  private updateGridParam(effectArea: DragArea): void {
111    const gridWidth = this.mPageDesktopStyleConfig.mGridWidth;
112    const gridHeight = this.mPageDesktopStyleConfig.mGridHeight;
113    Log.showDebug(TAG, `updateGridParam gridWidth: ${gridWidth}, gridHeight: ${gridHeight}`);
114    this.mGridConfig = this.mSettingsModel.getGridConfig();
115    const column = this.mGridConfig.column;
116    const row = this.mGridConfig.row;
117    const columnsGap =  this.mPageDesktopStyleConfig.mColumnsGap;
118    const rowGap =  this.mPageDesktopStyleConfig.mRowsGap;
119    this.mGridItemHeight = row > 0 ? (gridHeight + columnsGap) / row : 0;
120    this.mGridItemWidth = column > 0 ? (gridWidth + rowGap) / column : 0;
121    Log.showDebug(TAG, `updateGridParam column: ${column}, row: ${row}`);
122    this.mPageCoordinateData.gridYAxis = [];
123    for (let i = 1; i <= row; i++) {
124      const touchPositioningY = (gridHeight / row) * i + effectArea.top;
125      this.mPageCoordinateData.gridYAxis.push(touchPositioningY);
126    }
127
128    this.mPageCoordinateData.gridXAxis = [];
129    for (let i = 1; i <= column; i++) {
130      const touchPositioningX = (gridWidth / column) * i + effectArea.left;
131      this.mPageCoordinateData.gridXAxis.push(touchPositioningX);
132    }
133  }
134
135  protected getDragRelativeData(): any {
136    const desktopDataInfo: {
137      appGridInfo: [[]]
138    } = AppStorage.get('appListInfo');
139    return desktopDataInfo.appGridInfo;
140  }
141
142  getItemIndex(x: number, y: number): number {
143    Log.showInfo(TAG, `getItemIndex x: ${x}, y: ${y}`);
144    let rowVal = CommonConstants.INVALID_VALUE;
145    for (let index = 0; index < this.mPageCoordinateData.gridYAxis.length; index++) {
146      if (this.mPageCoordinateData.gridYAxis[index] > y) {
147        rowVal = index;
148        break;
149      }
150    }
151    let columnVal = CommonConstants.INVALID_VALUE;
152    for (let index = 0; index < this.mPageCoordinateData.gridXAxis.length; index++) {
153      if (this.mPageCoordinateData.gridXAxis[index] > x) {
154        columnVal = index;
155        break;
156      }
157    }
158    const column = this.mGridConfig.column;
159    if (rowVal != CommonConstants.INVALID_VALUE && columnVal != CommonConstants.INVALID_VALUE) {
160      return rowVal * column + columnVal;
161    }
162    return CommonConstants.INVALID_VALUE;
163  }
164
165  protected getItemByIndex(index: number): any {
166    const column = index % this.mGridConfig.column;
167    const row = Math.floor(index / this.mGridConfig.column);
168    const pageIndex: number = AppStorage.get('pageIndex');
169    const appGridInfo = this.getDragRelativeData();
170    Log.showInfo(TAG, `getItemByIndex pageIndex: ${pageIndex}, appGridInfo length: ${appGridInfo.length},
171    column: ${column}, row: ${row}`);
172    const itemInfo = appGridInfo[pageIndex].find(item => {
173      if (item.typeId == CommonConstants.TYPE_APP) {
174        return item.column == column && item.row == row;
175      } else if (item.typeId == CommonConstants.TYPE_FOLDER || item.typeId == CommonConstants.TYPE_CARD) {
176        return this.isItemInRowColumn(row, column, item);
177      }
178    });
179    return itemInfo;
180  }
181
182  /**
183   * judge the item is at target row and column
184   * @param row
185   * @param column
186   * @param item
187   */
188  private isItemInRowColumn(row: number, column: number, item: any): boolean {
189    return item.column <= column && column < item.column + item.area[0] && item.row <= row && row < item.row + item.area[1];
190  }
191
192  getTouchPosition(x: number, y: number): DragItemPosition {
193    const pageIndex: number = AppStorage.get('pageIndex');
194    Log.showDebug(TAG, `getTouchPosition pageIndex: ${pageIndex}`);
195    const position: DragItemPosition = {
196      page: pageIndex,
197      row: 0,
198      column: 0,
199      X: x,
200      Y: y,
201    };
202    for (let i = 0; i < this.mPageCoordinateData.gridXAxis.length; i++) {
203      if (x < this.mPageCoordinateData.gridXAxis[i]) {
204        position.column = i;
205        break;
206      } else {
207        position.column = this.mPageCoordinateData.gridXAxis.length - 1;
208      }
209    }
210    for (let i = 0; i < this.mPageCoordinateData.gridYAxis.length; i++) {
211      if (y < this.mPageCoordinateData.gridYAxis[i]) {
212        position.row = i;
213        break;
214      } else {
215        position.row = this.mPageCoordinateData.gridYAxis.length - 1;
216      }
217    }
218    return position;
219  }
220
221  onDragStart(x: number, y: number): void {
222    this.mStartPosition = null;
223    Log.showInfo(TAG, 'onDragStart start');
224    const selectAppIndex = this.getItemIndex(x, y);
225    AppStorage.setOrCreate('selectAppIndex', selectAppIndex);
226    this.mStartPosition = this.getTouchPosition(x, y);
227  }
228
229  onDragDrop(x: number, y: number): boolean {
230    const dragItemInfo: LauncherDragItemInfo = AppStorage.get<LauncherDragItemInfo>('dragItemInfo');
231    if (!dragItemInfo.isDragging) {
232      return false;
233    }
234    const dragItemType: number = AppStorage.get('dragItemType');
235    const deviceType: string = AppStorage.get('deviceType')
236    // dock appInfo has no location information.
237    if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
238      dragItemInfo.typeId = CommonConstants.TYPE_APP;
239      dragItemInfo.area = [1, 1];
240      dragItemInfo.page = AppStorage.get('pageIndex');
241    }
242    Log.showDebug(TAG, `onDragEnd dragItemInfo: ${JSON.stringify(dragItemInfo)}`);
243    const endIndex = this.getItemIndex(x, y);
244    const startPosition: DragItemPosition = this.copyPosition(this.mStartPosition);
245    let endPosition: DragItemPosition = null;
246    this.mEndPosition = this.getTouchPosition(x, y);
247    Log.showDebug(TAG, `onDragEnd mEndPosition: ${JSON.stringify(this.mEndPosition)}`);
248    endPosition = this.copyPosition(this.mEndPosition);
249    const info = this.mSettingsModel.getLayoutInfo();
250    const layoutInfo = info.layoutInfo;
251    if (dragItemInfo.typeId == CommonConstants.TYPE_FOLDER || dragItemInfo.typeId == CommonConstants.TYPE_CARD) {
252      this.updateEndPosition(dragItemInfo);
253    } else {
254      if (this.isMoveToSamePosition(dragItemInfo)) {
255        this.deleteBlankPageAfterDragging(startPosition, endPosition);
256        return false;
257      }
258      const endLayoutInfo = this.getEndLayoutInfo(layoutInfo);
259      if (endLayoutInfo != undefined) {
260        // add app to folder
261        if (endLayoutInfo.typeId === CommonConstants.TYPE_FOLDER) {
262          this.mBigFolderViewModel.addOneAppToFolder(dragItemInfo, endLayoutInfo.folderId);
263          if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
264            localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
265          }
266          this.deleteBlankPageAfterDragging(startPosition, endPosition);
267          return true;
268        } else if (endLayoutInfo.typeId === CommonConstants.TYPE_APP) {
269          // create a new folder
270          const layoutInfoList = [endLayoutInfo];
271          let startLayoutInfo = null;
272          if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
273            let appInfoList = this.mSettingsModel.getAppListInfo();
274            const appIndex = appInfoList.findIndex(item => {
275              return item.keyName === dragItemInfo.keyName;
276            })
277            if (appIndex == CommonConstants.INVALID_VALUE) {
278              appInfoList.push({
279                "appName": dragItemInfo.appName,
280                "isSystemApp": dragItemInfo.isSystemApp,
281                "isUninstallAble": dragItemInfo.isUninstallAble,
282                "appIconId": dragItemInfo.appIconId,
283                "appLabelId": dragItemInfo.appLabelId,
284                "bundleName": dragItemInfo.bundleName,
285                "abilityName": dragItemInfo.abilityName,
286                "moduleName": dragItemInfo.moduleName,
287                "keyName": dragItemInfo.keyName,
288                "typeId": dragItemInfo.typeId,
289                "area": dragItemInfo.area,
290                "page": dragItemInfo.page,
291                "column": this.getColumn(endIndex),
292                "row": this.getRow(endIndex),
293                "x": 0,
294                "installTime": dragItemInfo.installTime,
295                "badgeNumber": dragItemInfo.badgeNumber
296              })
297              this.mSettingsModel.setAppListInfo(appInfoList);
298            }
299            startLayoutInfo = dragItemInfo;
300            localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
301          } else {
302            startLayoutInfo = this.getStartLayoutInfo(layoutInfo, dragItemInfo);
303          }
304          layoutInfoList.push(startLayoutInfo);
305          this.mBigFolderViewModel.addNewFolder(layoutInfoList).then(()=> {
306            this.deleteBlankPageAfterDragging(startPosition, endPosition);
307          });
308          return true;
309        } else if (endLayoutInfo.typeId === CommonConstants.TYPE_CARD) {
310          if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
311            localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_ITEM_ADD, dragItemInfo);
312            localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
313            localEventManager.sendLocalEventSticky(EventConstants.EVENT_SMARTDOCK_INIT_FINISHED, null);
314            return true;
315          }
316        }
317      }
318    }
319
320    if (dragItemType === CommonConstants.DRAG_FROM_DOCK && deviceType == CommonConstants.DEFAULT_DEVICE_TYPE) {
321      let appInfoTemp = {
322        "bundleName": dragItemInfo.bundleName,
323        "typeId": dragItemInfo.typeId,
324        "abilityName": dragItemInfo.abilityName,
325        "moduleName": dragItemInfo.moduleName,
326        "keyName": dragItemInfo.keyName,
327        "area": dragItemInfo.area,
328        "page": dragItemInfo.page,
329        "column": this.getColumn(endIndex),
330        "row": this.getRow(endIndex),
331        "badgeNumber": dragItemInfo.badgeNumber
332      };
333      layoutInfo.push(appInfoTemp as LauncherDragItemInfo);
334      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_RESIDENT_DOCK_ITEM_DELETE, dragItemInfo);
335    } else {
336      this.checkAndMove(this.mStartPosition, this.mEndPosition, layoutInfo, dragItemInfo);
337    }
338
339    info.layoutInfo = layoutInfo;
340    this.mSettingsModel.setLayoutInfo(info);
341    localEventManager.sendLocalEventSticky(EventConstants.EVENT_SMARTDOCK_INIT_FINISHED, null);
342    this.deleteBlankPageAfterDragging(startPosition, endPosition);
343    return true;
344  }
345
346  private updateEndPosition(dragItemInfo: LauncherDragItemInfo): void {
347    this.mGridConfig = this.mSettingsModel.getGridConfig();
348    if (this.mEndPosition.row < 0) {
349      this.mEndPosition.row = 0;
350    } else if (this.mEndPosition.row + dragItemInfo.area[1] > this.mGridConfig.row) {
351      this.mEndPosition.row = this.mGridConfig.row - dragItemInfo.area[1];
352    }
353    if (this.mEndPosition.column < 0) {
354      this.mEndPosition.column = 0;
355    } else if (this.mEndPosition.column + dragItemInfo.area[0] > this.mGridConfig.column ) {
356      this.mEndPosition.column = this.mGridConfig.column - dragItemInfo.area[0];
357    }
358  }
359
360  deleteBlankPageOutsideEffect() {
361    // delete Blank Page because of drag outside effect area
362    Log.showInfo(TAG, 'deleteBlankPageOutsideEffect' );
363    const startPosition: DragItemPosition = this.copyPosition(this.mStartPosition);
364    this.deleteBlankPageAfterDragging(startPosition, startPosition);
365  }
366
367  /**
368   * copy a new position object by original position
369   *
370   * @param position - original position
371   */
372  private copyPosition(position: DragItemPosition): DragItemPosition {
373    if (CheckEmptyUtils.isEmpty(position)) {
374      return null;
375    }
376    const directionPosition: DragItemPosition = {
377      page: position.page,
378      row: position.row,
379      column: position.column,
380      X: position.X,
381      Y: position.Y
382    };
383    return directionPosition;
384  }
385
386  /**
387   * delete blank page after dragging
388   *
389   * @param startPosition - drag start position
390   * @param endPosition - drag end position
391   */
392  deleteBlankPageAfterDragging(startPosition: DragItemPosition, endPosition: DragItemPosition): void {
393    const layoutInfo = this.mSettingsModel.getLayoutInfo();
394    const pageCount = layoutInfo.layoutDescription.pageCount;
395    const isAddByDraggingFlag = this.mPageDesktopModel.isAddByDragging();
396    let deleteLastFlag = false;
397    if (isAddByDraggingFlag && (CheckEmptyUtils.isEmpty(endPosition) ||
398    !CheckEmptyUtils.isEmpty(endPosition) && endPosition.page != pageCount - 1 )) {
399      layoutInfo.layoutDescription.pageCount = pageCount - 1;
400      deleteLastFlag = true;
401    }
402    let deleteStartFlag = false;
403    if (!CheckEmptyUtils.isEmpty(startPosition)) {
404      deleteStartFlag = this.mPageDesktopModel.deleteBlankPageFromLayoutInfo(layoutInfo, startPosition.page);
405    }
406    if (CheckEmptyUtils.isEmpty(endPosition) || JSON.stringify(startPosition) === JSON.stringify(endPosition)) {
407      Log.showDebug(TAG, `pageIndex: ${JSON.stringify(startPosition) === JSON.stringify(endPosition)}`);
408      AppStorage.setOrCreate('pageIndex', startPosition.page);
409    } else if (deleteStartFlag) {
410      if (startPosition.page > endPosition.page) {
411        AppStorage.setOrCreate('pageIndex', endPosition.page);
412      } else if (endPosition.page > startPosition.page &&
413      endPosition.page < layoutInfo.layoutDescription.pageCount) {
414        AppStorage.setOrCreate('pageIndex', endPosition.page - 1);
415      }
416    }
417    this.mPageDesktopModel.setAddByDragging(false);
418    if (deleteLastFlag || deleteStartFlag) {
419      this.mSettingsModel.setLayoutInfo(layoutInfo);
420      localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_PAGEDESK_REFRESH, null);
421    }
422  }
423
424  private isMoveToSamePosition(dragItemInfo: LauncherDragItemInfo): boolean {
425    if (this.mEndPosition.page == dragItemInfo.page &&
426    this.mEndPosition.row == dragItemInfo.row && this.mEndPosition.column == dragItemInfo.column) {
427      return true;
428    }
429    return false;
430  }
431
432  private getEndLayoutInfo(layoutInfo) {
433    const endLayoutInfo = layoutInfo.find(item => {
434      if (item.typeId == CommonConstants.TYPE_FOLDER || item.typeId == CommonConstants.TYPE_CARD) {
435        return item.page === this.mEndPosition.page && this.isItemInRowColumn(this.mEndPosition.row, this.mEndPosition.column, item);
436      } else if (item.typeId == CommonConstants.TYPE_APP) {
437        return item.page === this.mEndPosition.page && item.row === this.mEndPosition.row && item.column === this.mEndPosition.column;
438      }
439    });
440    return endLayoutInfo;
441  }
442
443  private getStartLayoutInfo(layoutInfo, dragItemInfo: LauncherDragItemInfo): LauncherDragItemInfo {
444    const startLayoutInfo = layoutInfo.find(item => {
445      return item.page === dragItemInfo.page && item.row === dragItemInfo.row && item.column === dragItemInfo.column;
446    });
447    return startLayoutInfo;
448  }
449
450  /**
451   * folder squeeze handle
452   * @param source
453   * @param destination
454   * @param layoutInfo
455   * @param dragItemInfo
456   */
457  private checkAndMove(source, destination, layoutInfo, dragItemInfo: LauncherDragItemInfo): boolean {
458    Log.showDebug(TAG, 'checkAndMove start');
459
460    const allPositions = this.getAllPositions(destination, layoutInfo);
461    const objectPositionCount = this.getObjectPositionCount(destination, layoutInfo);
462    const pressedPositions = this.getPressedObjects(destination, allPositions, dragItemInfo);
463
464    // source and destination is in the same page
465    if (source.page == destination.page && !this.checkCanMoveInSamePage(pressedPositions, objectPositionCount, dragItemInfo)) {
466      return false;
467    }
468    // source and destination is in diff page
469    if (source.page != destination.page && !this.checkCanMoveInDiffPage(allPositions, dragItemInfo)) {
470      return false;
471    }
472
473    if (source.page == destination.page) {
474      this.setSourcePositionToNull(dragItemInfo, allPositions);
475    }
476    this.setDestinationPosition(destination, allPositions, dragItemInfo);
477
478    Log.showDebug(TAG, `checkAndMove pressedPositions.foldersAndForms: ${pressedPositions.foldersAndForms.length}`);
479    if (pressedPositions.foldersAndForms.length != 0) {
480      if (!this.moveFoldersAndForms(pressedPositions.foldersAndForms, destination, allPositions, dragItemInfo)) {
481        return false;
482      }
483    }
484    Log.showDebug(TAG, `checkAndMove pressedPositions.apps.length: ${pressedPositions.apps.length}`);
485    if (pressedPositions.apps.length != 0) {
486      this.moveApps(pressedPositions.apps, allPositions);
487    }
488    Log.showDebug(TAG, 'checkAndMove update destination ');
489    this.updateDestinationByDragItem(dragItemInfo, destination, allPositions);
490    Log.showDebug(TAG, 'checkAndMove update layoutInfo ');
491    for (let index = 0; index < layoutInfo.length; index++) {
492      for (let row = allPositions.length - 1; row >= 0 ; row--) {
493        for (let column = allPositions[row].length - 1; column >= 0 ; column--) {
494          if (layoutInfo[index].typeId == CommonConstants.TYPE_APP && layoutInfo[index].keyName == allPositions[row][column].keyName ||
495          layoutInfo[index].typeId == CommonConstants.TYPE_FOLDER && layoutInfo[index].folderId == allPositions[row][column].keyName ||
496          layoutInfo[index].typeId == CommonConstants.TYPE_CARD && layoutInfo[index].cardId == allPositions[row][column].keyName) {
497            layoutInfo[index].row = row;
498            layoutInfo[index].column = column;
499            layoutInfo[index].page = destination.page;
500          }
501        }
502      }
503    }
504    Log.showDebug(TAG, 'checkAndMove end');
505    return true;
506  }
507
508  /**
509   * get desktop's position info
510   * @param destination
511   * @param layoutInfo
512   */
513  private getAllPositions(destination, layoutInfo): any[] {
514    Log.showDebug(TAG, 'getAllPositions start');
515    const allPositions = [];
516    this.setAllpositionsToNull(allPositions);
517
518    // set position in layoutInfo to all positions
519    for (let i = 0; i < layoutInfo.length; i++) {
520      if (layoutInfo[i].page == destination.page) {
521        if (layoutInfo[i].typeId == CommonConstants.TYPE_FOLDER || layoutInfo[i].typeId == CommonConstants.TYPE_CARD) {
522          let keyName = '';
523          if (layoutInfo[i].typeId == CommonConstants.TYPE_FOLDER) {
524            keyName = layoutInfo[i].folderId;
525          } else if (layoutInfo[i].typeId == CommonConstants.TYPE_CARD) {
526            keyName = layoutInfo[i].cardId;
527          }
528
529          const positionInLayoutInfo = {
530            typeId: layoutInfo[i].typeId,
531            keyName: keyName,
532            area: layoutInfo[i].area
533          };
534          for (let k = 0; k < layoutInfo[i].area[1]; k++) {
535            for (let j = 0; j < layoutInfo[i].area[0]; j++) {
536              allPositions[layoutInfo[i].row + k][layoutInfo[i].column + j] = positionInLayoutInfo;
537            }
538          }
539        } else if (layoutInfo[i].typeId == CommonConstants.TYPE_APP) {
540          const positionInLayoutInfo = {
541            typeId: layoutInfo[i].typeId,
542            keyName: layoutInfo[i].keyName,
543            area: layoutInfo[i].area
544          };
545          allPositions[layoutInfo[i].row][layoutInfo[i].column] = positionInLayoutInfo;
546        }
547      }
548    }
549    return allPositions;
550  }
551
552  private setAllpositionsToNull(allPositions): void {
553    const mGridConfig = this.mSettingsModel.getGridConfig();
554    const pageRow = mGridConfig.row;
555    const pageColumn = mGridConfig.column;
556
557    // set null to all positions in current page
558    for (let row = 0; row < pageRow; row++) {
559      const rowPositions = [];
560      for (let column = 0; column < pageColumn; column++) {
561        const position = {
562          typeId: -1,
563          keyName: 'null',
564          area: []
565        };
566        rowPositions.push(position);
567      }
568      allPositions.push(rowPositions);
569    }
570  }
571
572  /**
573   * get desktop's position count by Object
574   * @param destination
575   * @param layoutInfo
576   */
577  private getObjectPositionCount(destination, layoutInfo): Map<string, number> {
578    Log.showDebug(TAG, 'getObjectPositionCount start');
579
580    const objectPositionCount = new Map<string, number>();
581    // set position in layoutInfo to all positions
582    for (let i = 0; i < layoutInfo.length; i++) {
583      if (layoutInfo[i].page == destination.page) {
584        const count = layoutInfo[i].area[0] * layoutInfo[i].area[1];
585        let keyName = '';
586        if (layoutInfo[i].typeId == CommonConstants.TYPE_FOLDER) {
587          keyName = layoutInfo[i].folderId;
588        } else if (layoutInfo[i].typeId == CommonConstants.TYPE_CARD) {
589          keyName = layoutInfo[i].cardId;
590        } else if (layoutInfo[i].typeId == CommonConstants.TYPE_APP) {
591          keyName = layoutInfo[i].keyName;
592        }
593        objectPositionCount.set(keyName, count);
594      }
595    }
596    return objectPositionCount;
597  }
598
599  /**
600   * get Object under pressed big folder or form
601   * @param destination
602   * @param allPositions
603   * @param dragItemInfo
604   */
605  private getPressedObjects(destination, allPositions, dragItemInfo: LauncherDragItemInfo): {
606    apps: Array<LauncherDragItemInfo>,
607    foldersAndForms: Array<LauncherDragItemInfo>
608  } {
609    Log.showDebug(TAG, 'getPressedObjects start');
610    const row = destination.row;
611    const column = destination.column;
612    const apps = [];
613    const foldersAndForms = [];
614
615    Log.showDebug(TAG, `getPressedObjects destination.row: ${row},destination.column:${column}`);
616
617    for (let j = 0; j < dragItemInfo.area[1]; j++) {
618      for (let i = 0; i < dragItemInfo.area[0]; i++) {
619        if (allPositions[row + j][column + i].typeId == CommonConstants.TYPE_APP) {
620          apps.push(allPositions[row + j][column + i]);
621        } else if (allPositions[row + j][column + i].typeId == CommonConstants.TYPE_FOLDER &&
622        allPositions[row + j][column + i].keyName != dragItemInfo.folderId) {
623          foldersAndForms.push(allPositions[row + j][column + i]);
624        } else if (allPositions[row + j][column + i].typeId == CommonConstants.TYPE_CARD &&
625        allPositions[row + j][column + i].keyName != dragItemInfo.cardId) {
626          foldersAndForms.push(allPositions[row + j][column + i]);
627        }
628      }
629    }
630
631    Log.showDebug(TAG, `getPressedObjects foldersAndForms.length: ${foldersAndForms.length}`);
632    Log.showDebug(TAG, `getPressedObjects apps.length: ${apps.length}`);
633    const pressedObjects = {
634      apps,
635      foldersAndForms
636    };
637    return pressedObjects;
638  }
639
640  /**
641   * check of canMove in same page
642   * @param pressedPositions
643   * @param objectPositionCount
644   * @param dragItemInfo
645   */
646  private checkCanMoveInSamePage(pressedPositions, objectPositionCount, dragItemInfo: LauncherDragItemInfo): boolean {
647    Log.showDebug(TAG, 'checkCanMoveInSamePage start');
648    const foldersAndForms = pressedPositions.foldersAndForms;
649    if (foldersAndForms.length == 0) {
650      return true;
651    }
652
653    if (foldersAndForms.length == 1) {
654      if (dragItemInfo.typeId == CommonConstants.TYPE_APP && foldersAndForms[0].typeId == CommonConstants.TYPE_CARD) {
655        return true;
656      }
657    }
658
659    const coverPositionCount = new Map<string, number>();
660    Log.showDebug(TAG, `checkCanMoveInSamePage foldersAndForms.length: ${foldersAndForms.length}`);
661    for (let i = 0; i < foldersAndForms.length; i++) {
662      const keyName = foldersAndForms[i].keyName;
663      if (coverPositionCount.has(keyName)) {
664        coverPositionCount.set(keyName, coverPositionCount.get(keyName) + 1);
665      } else {
666        coverPositionCount.set(keyName, 1);
667      }
668    }
669
670    for (const keyName of coverPositionCount.keys()) {
671      if (coverPositionCount.get(keyName) < objectPositionCount.get(keyName) / 2) {
672        Log.showDebug(TAG, 'checkCanMoveInSamePage end false');
673        return false;
674      }
675    }
676    return true;
677  }
678
679  /**
680   * check of canMove in diff page
681   * @param allPositions
682   */
683  private checkCanMoveInDiffPage(allPositions, dragItemInfo): boolean {
684    Log.showDebug(TAG, 'checkCanMoveInDiffPage start');
685    let count = 0;
686    for (let i = 0; i < allPositions.length; i++) {
687      for (let j = 0; j < allPositions[i].length; j++) {
688        if (allPositions[i][j].typeId == -1) {
689          count++;
690        }
691      }
692    }
693    const minCount = dragItemInfo.area[0] * dragItemInfo.area[1];
694    // target page empty position min is dragItemInfo's need position
695    if (count < minCount) {
696      Log.showDebug(TAG, 'checkCanMoveInDiffPage end false');
697      return false;
698    }
699    return true;
700  }
701
702  /**
703   * set source‘s position to null
704   * @param source
705   * @param allPositions
706   */
707  private setSourcePositionToNull(dragItemInfo, allPositions): void {
708    Log.showDebug(TAG, 'setSourcePositionToNull start');
709    for (let j = 0; j < dragItemInfo.area[1]; j++) {
710      for (let i = 0; i < dragItemInfo.area[0]; i++) {
711        const nullPosition = {
712          typeId: -1,
713          keyName: 'null',
714          area: []
715        };
716        allPositions[dragItemInfo.row + j][dragItemInfo.column + i] = nullPosition;
717      }
718    }
719  }
720
721  /**
722   * set direction‘s position to null
723   * @param destination
724   * @param allPositions
725   * @param dragItemInfo
726   */
727  private setDestinationPosition(destination, allPositions, dragItemInfo): void {
728    Log.showDebug(TAG, 'setDestinationPosition start');
729    let keyName = '';
730    if (dragItemInfo.typeId == CommonConstants.TYPE_FOLDER) {
731      keyName = dragItemInfo.folderId;
732    } else if (dragItemInfo.typeId == CommonConstants.TYPE_CARD) {
733      keyName = dragItemInfo.cardId;
734    } else if (dragItemInfo.typeId == CommonConstants.TYPE_APP) {
735      keyName = dragItemInfo.keyName;
736    }
737
738    for (let j = 0; j < dragItemInfo.area[1]; j++) {
739      for (let i = 0; i < dragItemInfo.area[0]; i++) {
740        if (allPositions[destination.row + j][destination.column+ i].typeId == -1) {
741          const destinationPosition = {
742            typeId: dragItemInfo.typeId,
743            keyName: keyName,
744            area: dragItemInfo.area
745          };
746          allPositions[destination.row + j][destination.column+ i] = destinationPosition;
747        }
748      }
749    }
750  }
751
752  /**
753   * move folders and forms to target position
754   * @param foldersAndForms
755   * @param destination
756   * @param allPositions
757   * @param dragItemInfo
758   */
759  private moveFoldersAndForms(foldersAndForms, destination, allPositions, dragItemInfo: LauncherDragItemInfo): boolean {
760    Log.showDebug(TAG, 'moveFoldersAndForms start');
761    const movedFoldersAndForms = [];
762    for (let i = 0; i < foldersAndForms.length; i++) {
763      const moveFolderOrForm = foldersAndForms[i];
764      if (movedFoldersAndForms.indexOf(moveFolderOrForm.keyName) != -1) {
765        continue;
766      }
767
768      for (let row = 0; row < allPositions.length; row++) {
769        for (let column = 0; column < allPositions[row].length; column++) {
770          if (moveFolderOrForm.keyName == allPositions[row][column].keyName) {
771            this.updateDestinationToNull(dragItemInfo, destination, allPositions, row, column);
772          }
773        }
774      }
775
776      let isUsablePosition = false;
777      for (let row = 0; row < allPositions.length; row++) {
778        if (isUsablePosition) {
779          break;
780        }
781        for (let column = 0; column < allPositions[row].length; column++) {
782          const usablePositionCount =this.getUsablePositionCount(moveFolderOrForm, allPositions, row , column);
783          if (usablePositionCount == moveFolderOrForm.area[1] * moveFolderOrForm.area[0]) {
784            isUsablePosition = true;
785            this.updatePositionByMoveItem(moveFolderOrForm, allPositions, row, column);
786            movedFoldersAndForms.push(moveFolderOrForm.keyName);
787            break;
788          }
789        }
790      }
791      if (!isUsablePosition) {
792        Log.showDebug(TAG, 'moveFoldersAndForms return false');
793        return false;
794      }
795    }
796    this.updateDestinationByDragItem(dragItemInfo, destination, allPositions);
797    return true;
798  }
799
800  /**
801   * get the count of usable position
802   * @param moveFolderOrForm
803   * @param allPositions
804   * @param row
805   * @param column
806   */
807  private getUsablePositionCount(moveFolderOrForm, allPositions, row , column): number {
808    let getUsablePositionCount = 0;
809    for (let j = 0; j < moveFolderOrForm.area[1]; j++) {
810      for (let i = 0; i < moveFolderOrForm.area[0]; i++) {
811        if (row + j < allPositions.length && column + i < allPositions[row].length && allPositions[row + j][column + i].typeId == -1) {
812          getUsablePositionCount++;
813        }
814      }
815    }
816    return getUsablePositionCount;
817  }
818
819  /**
820   * update destination to nullPosition
821   *
822   * @param dragItemInfo
823   * @param destination
824   * @param allPositions
825   * @param row
826   * @param column
827   */
828  private updateDestinationToNull(dragItemInfo: LauncherDragItemInfo, destination, allPositions, row, column): void {
829    let destinationFlag = false;
830    for (let j = 0; j < dragItemInfo.area[1]; j++) {
831      if (destinationFlag) {
832        break;
833      }
834      for (let i = 0; i < dragItemInfo.area[0]; i++) {
835        if (destination.row + j == row && destination.column + i == column) {
836          destinationFlag = true;
837          break;
838        }
839      }
840    }
841    if (!destinationFlag) {
842      const nullPosition = {
843        typeId: -1,
844        keyName: 'null',
845        area: []
846      };
847      allPositions[row][column] = nullPosition;
848    }
849  }
850
851  /**
852   * update destination position by dragItemInfo
853   *
854   * @param dragItemInfo
855   * @param destination
856   * @param allPositions
857   */
858  private updateDestinationByDragItem(dragItemInfo: LauncherDragItemInfo, destination, allPositions): void {
859    let keyName = '';
860    if (dragItemInfo.typeId == CommonConstants.TYPE_FOLDER) {
861      keyName = dragItemInfo.folderId;
862    } else if (dragItemInfo.typeId == CommonConstants.TYPE_CARD) {
863      keyName = dragItemInfo.cardId >= 0 ? String(dragItemInfo.cardId) : '';
864    } else if (dragItemInfo.typeId == CommonConstants.TYPE_APP) {
865      keyName = dragItemInfo.keyName;
866    }
867
868    for (let j = 0; j < dragItemInfo.area[1]; j++) {
869      for (let i = 0; i < dragItemInfo.area[0]; i++) {
870        const dragItemPosition = {
871          typeId: dragItemInfo.typeId,
872          keyName: keyName,
873          area: dragItemInfo.area
874        };
875        allPositions[destination.row + j][destination.column + i] = dragItemPosition;
876      }
877    }
878  }
879
880  /**
881   * update positions by moveItemInfo
882   *
883   * @param moveFolderOrForm
884   * @param allPositions
885   * @param row
886   * @param column
887   */
888  private updatePositionByMoveItem(moveFolderOrForm, allPositions, row, column): void {
889    for (let j = 0; j < moveFolderOrForm.area[1]; j++) {
890      for (let i = 0; i < moveFolderOrForm.area[0]; i++) {
891        const movePosition = {
892          typeId: moveFolderOrForm.typeId,
893          keyName: moveFolderOrForm.keyName,
894          area: moveFolderOrForm.area
895        };
896        allPositions[row + j][column + i] = movePosition;
897      }
898    }
899  }
900
901  /**
902   * move apps to target position
903   * @param apps
904   * @param allPositions
905   */
906  private moveApps(apps, allPositions): void {
907    Log.showDebug(TAG, 'moveApps start');
908    for (let i = 0; i < apps.length; i++) {
909      const app = apps[i];
910      let isUsable = false;
911      for (let row = 0; row < allPositions.length; row++) {
912        if (isUsable) {
913          break;
914        }
915        for (let column = 0; column < allPositions[row].length; column++) {
916          if (allPositions[row][column].typeId == -1) {
917            const appPosition = {
918              typeId: app.typeId,
919              keyName: app.keyName,
920              area: app.area
921            };
922            allPositions[row][column] = appPosition;
923            isUsable = true;
924            break;
925          }
926        }
927      }
928    }
929  }
930}
931