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