• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 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 { TimelineData } from './photo/TimelineData';
17import { Log } from '../../utils/Log';
18import { MediaItem } from './photo/MediaItem';
19import type { AsyncCallback } from '../common/AsyncCallback';
20import { BrowserDataFactory } from '../../interface/BrowserDataFactory';
21import type { BrowserDataInterface } from '../../interface/BrowserDataInterface';
22import { Constants } from '../common/Constants';
23
24const TAG: string = 'common_SelectManager';
25
26export class BucketSelectionEntry {
27  private groupId = -1;
28  private clickedSet: Set<string> = new Set();
29  private totalCount = 0;
30  private inverseSelection = false;
31  private groupSelect = false;
32
33  public setGroupId(groupId: number): void {
34    this.groupId = groupId;
35  }
36
37  public getGroupId(): number {
38    return this.groupId;
39  }
40
41  public setTotalCount(totalCount: number): void {
42    this.totalCount = totalCount;
43  }
44
45  public getTotalCount(): number {
46    return this.totalCount;
47  }
48
49  public setGroupSelect(selectMode: boolean): void {
50    this.groupSelect = selectMode;
51  }
52
53  public getGroupSelect(): boolean {
54    return this.groupSelect;
55  }
56
57  public getClickSet(): Set<string> {
58    return this.clickedSet;
59  }
60
61  public getSelectedCount(): number {
62    if (this.inverseSelection) {
63      return this.totalCount - this.clickedSet.size;
64    }
65    return this.clickedSet.size;
66  }
67
68  public selectAll(): void {
69    this.inverseSelection = true;
70    this.groupSelect = true;
71    this.clickedSet.clear();
72  }
73
74  public deSelectAll(): void {
75    this.inverseSelection = false;
76    this.groupSelect = false;
77    this.clickedSet.clear();
78  }
79
80  public isItemSelected(targetId: string): boolean {
81    return (this.inverseSelection != this.clickedSet.has(targetId));
82  }
83
84  public inSelectAllMode(): boolean {
85    return this.inverseSelection && (this.clickedSet.size == 0);
86  }
87
88  /**
89   * Change the select all status of the entry, depending on the total deselection status of the timeline
90   *
91   * @param isInverseMode The total inverse selection status of the timeline. If it is true,
92   * it is global inverse selection and requires reverse operation
93   */
94  public changeSelectMode(isInverseMode: boolean): void {
95      isInverseMode
96      ? (this.getSelectedCount() == 0 ? this.selectAll() : this.deSelectAll())
97      : (this.inSelectAllMode() ? this.deSelectAll() : this.selectAll())
98  }
99
100  public getInverseSelection(): boolean {
101    return this.inverseSelection;
102  }
103}
104
105export class ItemCoordinate {
106  groupId = -1;
107  subIndex = -1;
108
109  constructor() {
110  }
111
112  public setGroupId(id: number): ItemCoordinate {
113    this.groupId = id;
114    return this;
115  }
116
117  public getGroupId(): number {
118    return this.groupId;
119  }
120
121  public setIndex(index: number): ItemCoordinate {
122    this.subIndex = index;
123    return this;
124  }
125
126  public getIndex(): number {
127    return this.subIndex;
128  }
129}
130
131export class SelectManager {
132  mIsSelectedMode = false;
133  clickedSet: Set<string> = new Set();
134  totalCount = 0;
135  inverseSelection = false;
136  inSingleMode = false;
137  isAllSelected = false;
138  mCallbacks = new Map<string, Function>();
139  photoDataImpl: BrowserDataInterface;
140  selectManagerCallback: SelectManagerCallback;
141  albumUri = undefined;
142  deviceId = undefined;
143
144  constructor() {
145    this.selectManagerCallback = new SelectManagerCallback(this);
146  }
147
148  public setTotalCount(count: number): void {
149    this.totalCount = count;
150    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
151
152    if (this.isAllSelected) {
153      this.isAllSelected = false;
154      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
155    }
156    if (this.totalCount == this.getSelectedCount()) {
157      this.isAllSelected = true;
158      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
159    }
160  }
161
162  public setPhotoDataImpl(): void {
163    this.photoDataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO);
164  }
165
166  public setAlbumUri(albumUri): void {
167    this.albumUri = albumUri;
168  }
169
170  public setDeviceId(deviceId: string): void {
171    this.deviceId = deviceId;
172  }
173
174  public registerCallback(name: string, cb: Function): void {
175    this.mCallbacks.set(name, cb);
176  }
177
178  public unregisterCallback(name: string): void {
179    this.mCallbacks.delete(name);
180  }
181
182  public emitCallback(name: string, argument: unknown[]): void {
183    this.mCallbacks.has(name) && this.mCallbacks.get(name).apply(this, argument);
184  }
185
186  public toggle(targetId: string, isSelected: boolean, targetIndex?: number): boolean {
187    Log.info(TAG, `toggle ${targetId} ${isSelected} ${targetIndex}`);
188    if (targetId == undefined) {
189      return true;
190    }
191    if (isSelected == (!this.inverseSelection)) {
192      this.clickedSet.add(targetId);
193      Log.info(TAG, `add targetID ${targetId}`);
194    } else {
195      this.clickedSet.delete(targetId);
196    }
197    if (this.totalCount == this.getSelectedCount()) {
198      this.isAllSelected = true;
199      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
200    } else {
201      this.isAllSelected = false;
202      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
203    }
204    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
205    if (targetIndex !== undefined) {
206      this.mCallbacks.has('select') && this.mCallbacks.get('select')(targetIndex);
207    }
208    return true;
209  }
210
211  public selectAllWithoutNotify(reverseSelection: boolean, shouldCallSelectALl: boolean): void {
212    if (reverseSelection) {
213      this.inverseSelection = true;
214      this.clickedSet.clear();
215      this.isAllSelected = true;
216    } else {
217      this.isAllSelected = true;
218    }
219    AppStorage.SetOrCreate('focusUpdate', true);
220    if (shouldCallSelectALl) {
221      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
222    }
223    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
224  }
225
226  public selectAll(reverseSelection: boolean): void {
227    this.selectAllWithoutNotify(reverseSelection, true);
228  }
229
230  public deSelectAll(): void {
231    this.inverseSelection = false;
232    this.isAllSelected = false;
233    this.clickedSet.clear();
234    AppStorage.SetOrCreate('focusUpdate', true);
235    this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
236    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
237  }
238
239  public isItemSelected(targetId: string, index?: number): boolean {
240    Log.info(TAG, `isItemSelected ${targetId}, ${index}`);
241    return (this.inverseSelection != this.clickedSet.has(targetId));
242  }
243
244  public getSelectedCount(): number {
245    return (this.inverseSelection) ? this.totalCount - this.clickedSet.size : this.clickedSet.size;
246  }
247
248  public onModeChange(newMode: boolean): void {
249    if (newMode) {
250      this.mIsSelectedMode = true;
251    } else {
252      this.mIsSelectedMode = false;
253      this.inverseSelection = false;
254      this.isAllSelected = false;
255      this.clickedSet.clear();
256      AppStorage.SetOrCreate('focusUpdate', true);
257      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
258      this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
259    }
260  }
261
262  public getSelection(callback: AsyncCallback<string[]>): void {
263    if (this.inverseSelection) {
264      this.selectManagerCallback.setSubCallback(callback);
265      this.photoDataImpl.getData(this.selectManagerCallback, {
266        albumUri: this.albumUri
267      });
268    } else {
269      let result = [];
270      result = Array.from(this.clickedSet);
271      callback.callback(result);
272    }
273  }
274
275  public getDeleteSelection(callback: AsyncCallback<string[]>): void {
276    if (this.inverseSelection) {
277      this.selectManagerCallback.setSubCallback(callback);
278      this.photoDataImpl.getData(this.selectManagerCallback, {
279        albumUri: this.albumUri
280      });
281    } else {
282      let result = [];
283      result = Array.from(this.clickedSet);
284      callback.callback(result);
285    }
286  }
287
288  public async getSelectedItems(callback: Function): Promise<void> {
289    let result = new Array<MediaItem>();
290    Log.info(TAG, 'getSelectedItems');
291    await this.getItems(this.photoDataImpl, 0, this.totalCount, (temp: MediaItem[]) => {
292      temp.forEach((item) => {
293        if (this.inverseSelection) {
294          Log.info(TAG, 'getSelectedItems inverseSelection');
295          if (item && !this.clickedSet.has(item.uri)) {
296            result.push(item);
297          }
298        } else {
299          if (this.clickedSet.has(item.uri)) {
300            result.push(item);
301          }
302        }
303      })
304      Log.info(TAG, `enter callback result ${result.length}`);
305      callback(result);
306    })
307  }
308
309  public handleSelection(mediaItems: MediaItem[], callback: AsyncCallback<string[]>): void {
310    let result = [];
311    mediaItems.forEach((mediaItem) => {
312      if (mediaItem && !this.clickedSet.has(mediaItem.uri)) {
313        result.push(mediaItem.uri);
314      }
315    })
316    callback.callback(result);
317  }
318
319  public async getItems(photoDataImpl: BrowserDataInterface,
320                        start: number, count: number, callbackFunc: Function): Promise<void> {
321    Log.info(TAG, `getItems start: ${start} count: ${count}`);
322    let cb: AsyncCallback<MediaItem[]> = {
323      callback: (t: MediaItem[]) => {
324        //注意命名不要冲突
325        callbackFunc(t);
326      }
327    }
328    photoDataImpl.getData(cb, { albumUri: this.albumUri, start: start, count: count });
329  }
330
331  public getClassName(): string {
332    return 'SelectManager';
333  }
334}
335
336class SelectManagerCallback implements AsyncCallback<MediaItem[]> {
337  source: SelectManager;
338  subCallback: AsyncCallback<string[]>;
339
340  constructor(source: SelectManager) {
341    this.source = source;
342  }
343
344  public setSubCallback(cb: AsyncCallback<string[]>): void {
345    this.subCallback = cb;
346  }
347
348  public callback(mediaSetList: MediaItem[]): void {
349    this.source.handleSelection(mediaSetList, this.subCallback);
350  }
351}
352
353export class ThirdSelectManager extends SelectManager {
354  type: string;
355  isMultiPick: boolean;
356  selectedMap: Map<string, MediaItem> = new Map();
357  getMediaItemFunc: Function;
358  indexMap: Map<MediaItem, number> = new Map();
359
360  constructor() {
361    super();
362  }
363
364  public setGetMediaItemFunc(func: Function): void {
365    this.getMediaItemFunc = func;
366  }
367
368  public setType(type: string): void {
369    this.type = type;
370  }
371
372  public getType(): string {
373    return this.type;
374  }
375
376  public setIsMultiPick(isMultiPick: boolean): void {
377    this.isMultiPick = isMultiPick;
378  }
379
380  public getIsMultiPick(): boolean {
381    return this.isMultiPick;
382  }
383
384  public getSelectedSet(): Set<string> {
385    return super.clickedSet;
386  }
387
388  public toggle(targetId: string, isSelected: boolean, targetIndex?: number): boolean {
389    if (this.getMediaItemFunc) {
390      let containsUri = this.selectedMap.has(targetId);
391      if (isSelected && !containsUri) {
392        this.selectedMap.set(targetId, this.getMediaItemFunc(targetId));
393        this.indexMap.set(this.getMediaItemFunc(targetId), targetIndex);
394      }
395      if (!isSelected && containsUri) {
396        this.selectedMap.delete(targetId);
397        this.indexMap.delete(this.getMediaItemFunc(targetId));
398      }
399    }
400    return super.toggle(targetId, isSelected, targetIndex);
401  }
402
403  public deSelectAll(): void {
404    this.selectedMap.clear();
405    this.indexMap.clear();
406    super.deSelectAll();
407  }
408
409  public getSelectItemIndex(item: MediaItem): number {
410    let index = 0;
411    for (let selectItem of this.selectedMap.values()) {
412      if (item === selectItem) {
413        return index;
414      }
415      index++;
416    }
417    return Constants.INVALID;
418  }
419
420  public getSelectItemDataSourceIndex(item: MediaItem): number {
421    return this.indexMap.get(item) ? this.indexMap.get(item) : Constants.INVALID;
422  }
423
424  public getSelectItems(): Array<MediaItem> {
425    let itemArray = new Array<MediaItem>();
426    if (this.selectedMap.size === 0) {
427      return itemArray;
428    }
429    for (let item of this.selectedMap.values()) {
430      itemArray.push(item);
431    }
432    return itemArray;
433  }
434
435  public getClassName(): string {
436    return 'ThirdSelectManager';
437  }
438}
439
440export class TimelineSelectManager extends SelectManager {
441  mGroupData: TimelineData[] = [];
442  mSelectionEntryArray: BucketSelectionEntry[] = [];
443
444  constructor() {
445    super();
446  }
447
448  public selectAll(reverseSelection: boolean): void {
449    Log.info(TAG, `selectAll ${reverseSelection}`);
450    if (reverseSelection) {
451      this.inverseSelection = true;
452      this.clearEntryArray();
453      this.isAllSelected = true;
454    } else {
455      this.isAllSelected = true;
456    }
457    AppStorage.SetOrCreate('focusUpdate', true);
458    this.mCallbacks.has('updateGroupCount') && this.mCallbacks.get('updateGroupCount')();
459    this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
460    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
461  }
462
463  public deSelectAll(): void {
464    this.inverseSelection = false;
465    this.isAllSelected = false;
466    this.clearEntryArray();
467    AppStorage.SetOrCreate('focusUpdate', true);
468    this.mCallbacks.has('updateGroupCount') && this.mCallbacks.get('updateGroupCount')();
469    this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
470    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
471  }
472
473  public toggle(targetId: string, isSelected: boolean, targetIndex: number): boolean {
474    Log.info(TAG, `toggleTimeline ${targetIndex} id: ${targetId} ${isSelected}`);
475    let itemCoordinate = this.getCoordinateFromPosition(targetIndex);
476    let entry = this.getGroupEntry(itemCoordinate);
477    this.toggleClickSet(entry, targetId, isSelected);
478    let entrySelectedCount = entry.getSelectedCount();
479    Log.info(TAG, `check all selected ${entrySelectedCount} ${entry.getTotalCount()}`);
480
481    if (entrySelectedCount == entry.getTotalCount()) {
482      Log.info(TAG, 'group selectAll');
483      entry.selectAll();
484    }
485
486    this.mCallbacks.has('updateGroupCount') && this.mCallbacks.get('updateGroupCount')();
487
488    if (this.isAllSelected && (entrySelectedCount < entry.getTotalCount())) {
489      this.isAllSelected = false;
490      this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
491    }
492
493    if (this.getSelectedCount() == this.totalCount) {
494      this.isAllSelected = true;
495      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
496      this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
497    } else {
498      this.isAllSelected = false;
499      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
500      this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
501    }
502    return true;
503  }
504
505  public toggleGroup(itemCoordinate: ItemCoordinate): boolean {
506    Log.info(TAG, `check  toggleGroup: ${itemCoordinate.getGroupId()}`);
507    if (this.inverseSelection) {
508      let entry = this.mSelectionEntryArray[itemCoordinate.getGroupId()];
509      if (entry == undefined) {
510        entry = this.getGroupEntry(itemCoordinate);
511        entry.selectAll();
512      } else {
513        entry.changeSelectMode(true);
514      }
515    } else {
516      let entry = this.getGroupEntry(itemCoordinate);
517      entry.changeSelectMode(false);
518    }
519
520    let count = this.getSelectedCount();
521    if (count == this.totalCount) {
522      this.selectAll(false);
523    }
524    this.mCallbacks.has('updateGroupCount') && this.mCallbacks.get('updateGroupCount')();
525    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
526    if (this.getSelectedCount() == this.totalCount) {
527      this.isAllSelected = true;
528      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(true);
529    } else {
530      this.isAllSelected = false;
531      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
532    }
533    return true;
534  }
535
536  public getTitleCoordinate(position: number): ItemCoordinate {
537    return new ItemCoordinate().setGroupId(position).setIndex(-1);
538  }
539
540  public getSelectedCount(): number {
541    let count = 0;
542    this.mSelectionEntryArray.forEach((item) => {
543      count += item ? item.getSelectedCount() : 0;
544    })
545    if (this.inverseSelection) {
546      Log.info(TAG, `inverseSelection totalCount: ${this.totalCount - count}`);
547      return this.totalCount - count;
548    }
549    return count;
550  }
551
552  public onModeChange(newMode: boolean): void {
553    if (newMode) {
554      this.mIsSelectedMode = true;
555    } else {
556      this.mIsSelectedMode = false;
557      this.inverseSelection = false;
558      this.isAllSelected = false;
559      this.clearEntryArray();
560      AppStorage.SetOrCreate('focusUpdate', true);
561      this.mCallbacks.has('updateGroupCount') && this.mCallbacks.get('updateGroupCount')();
562      this.mCallbacks.has('allSelect') && this.mCallbacks.get('allSelect')(false);
563      this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
564    }
565  }
566
567  public isItemSelected(targetId: string, index: number): boolean {
568    let itemCoordinate = this.getCoordinateFromPosition(index);
569    let entry = this.mSelectionEntryArray[itemCoordinate.getGroupId()];
570    if (this.inverseSelection) {
571      return (entry == undefined) || (!entry.isItemSelected(targetId));
572    } else {
573      return (entry != undefined) && (entry.isItemSelected(targetId));
574    }
575  }
576
577  public isGroupSelected(index: number): boolean {
578    let entry = this.mSelectionEntryArray[index];
579    if (this.inverseSelection) {
580      return entry == null || entry.getSelectedCount() == 0;
581    } else {
582      return (entry != null) && (entry.inSelectAllMode());
583    }
584  }
585
586  public setGroupData(groupData: TimelineData[]): void {
587    if (groupData == undefined) {
588      return;
589    }
590    this.mGroupData = groupData;
591  }
592
593  public updateGroupData(groupData: TimelineData[]): void {
594    if (groupData == undefined) {
595      return;
596    }
597    this.mGroupData = groupData;
598    this.mSelectionEntryArray.forEach((entry: BucketSelectionEntry) => {
599      if (entry != undefined && (entry.getGroupId() < this.mGroupData.length)) {
600        entry.setTotalCount(this.mGroupData[entry.getGroupId()].count);
601      }
602    })
603    this.mCallbacks.has('updateCount') && this.mCallbacks.get('updateCount')(this.getSelectedCount());
604  }
605
606  public async getSelection(callback: AsyncCallback<string[]>): Promise<void> {
607    let result = new Array<string>();
608    let start = 0;
609    let doneCount = 0;
610    if (this.inverseSelection) {
611      for (let i = 0; i < this.mGroupData.length; i++) {
612        if (this.mSelectionEntryArray[i]) {
613          //全选模式下用户操作过的日期下的items根据用户选择反选
614          await this.getInverseSelectedFromEntry(this.mSelectionEntryArray[i],
615            start, this.mGroupData[i].count, (temp: string[]) => {
616              result = result.concat(temp);
617              Log.info(TAG, `getInverseSelectedFromEntry result ${result.length}`);
618              doneCount++;
619              this.checkIsGetSelectionFinish(doneCount, result, callback);
620            });
621        } else {
622          //全选模式下用户未操作过的日期下的items全量选中
623          await this.getItems(this.photoDataImpl, start, this.mGroupData[i].count, (temp: MediaItem[]) => {
624            temp.forEach((item) => {
625              result.push(item.uri);
626            });
627            Log.info(TAG, `getInverseGroupItems result ${result.length}`);
628            doneCount++;
629            this.checkIsGetSelectionFinish(doneCount, result, callback);
630          });
631        }
632        start += this.mGroupData[i].count;
633      }
634    } else {
635      for (let i = 0; i < this.mGroupData.length; i++) {
636        if (this.mSelectionEntryArray[i]) {
637          //正选模式下根据遍历日期分组用户选择正常取item
638          await this.getSelectedFromEntry(this.mSelectionEntryArray[i],
639            start, this.mGroupData[i].count, (temp: string[]) => {
640              Log.info(TAG, `getSelectedFromEntry result ${result.length}`);
641              result = result.concat(temp);
642              doneCount++;
643              this.checkIsGetSelectionFinish(doneCount, result, callback);
644            });
645        } else {
646          //正选模式下未操作过的分组直接跳过
647          doneCount++;
648          this.checkIsGetSelectionFinish(doneCount, result, callback);
649        }
650        start += this.mGroupData[i].count;
651      }
652    }
653  }
654
655  public async getSelectedItems(callback: Function): Promise<void> {
656    Log.info(TAG, 'getSelectedItems');
657    let result = new Array<MediaItem>();
658    let start = 0;
659    let doneCount = 0;
660    if (this.inverseSelection) {
661      Log.info(TAG, 'getSelectedItems: mode is inverseSelection');
662      for (let i = 0; i < this.mGroupData.length; i++) {
663        if (this.mSelectionEntryArray[i]) {
664          if (this.mSelectionEntryArray[i].getInverseSelection()) {
665            await this.getItems(this.photoDataImpl, start, this.mGroupData[i].count, (temp: MediaItem[]) => {
666              temp.forEach((item) => {
667                if (this.mSelectionEntryArray[i].getClickSet().has(item.uri)) {
668                  Log.debug(TAG, 'push one item');
669                  result.push(item);
670                }
671              });
672              doneCount++;
673              this.checkIsGetSelectionItemFinish(doneCount, result, callback);
674            });
675          } else {
676            await this.getItems(this.photoDataImpl, start, this.mGroupData[i].count, (temp: MediaItem[]) => {
677              temp.forEach((item) => {
678                if (!this.mSelectionEntryArray[i].getClickSet().has(item.uri)) {
679                  Log.debug(TAG, 'push one inverse item');
680                  result.push(item);
681                }
682              });
683              doneCount++;
684              this.checkIsGetSelectionItemFinish(doneCount, result, callback);
685            });
686          }
687        } else {
688          await this.getItems(this.photoDataImpl, start, this.mGroupData[i].count, (temp: MediaItem[]) => {
689            temp.forEach((item) => {
690              result.push(item);
691            });
692            doneCount++;
693            this.checkIsGetSelectionItemFinish(doneCount, result, callback);
694          });
695        }
696        start += this.mGroupData[i].count;
697      }
698    } else {
699      Log.info(TAG, 'getSelectedItems: mode is not inverseSelection');
700      for (let i = 0; i < this.mGroupData.length; i++) {
701        if (this.mSelectionEntryArray[i]) {
702          await this.getItems(this.photoDataImpl, start, this.mGroupData[i].count, (temp: MediaItem[]) => {
703            const entry = this.mSelectionEntryArray[i];
704            temp.forEach((item) => {
705              if (!entry.getInverseSelection()) {
706                if (entry.getClickSet().has(item.uri)) {
707                  Log.debug(TAG, 'push one item');
708                  result.push(item);
709                }
710              } else if (!entry.getClickSet().has(item.uri)) {
711                Log.debug(TAG, 'push one inverse item');
712                result.push(item);
713              }
714            });
715            doneCount++;
716            this.checkIsGetSelectionItemFinish(doneCount, result, callback);
717          });
718        } else {
719          doneCount++;
720          this.checkIsGetSelectionItemFinish(doneCount, result, callback);
721        }
722        start += this.mGroupData[i].count;
723      }
724    }
725  }
726
727  private toggleClickSet(entry: BucketSelectionEntry, targetId: string, isSelected: boolean): void {
728    Log.info(TAG, `toggleClickSet: ${targetId} + ${isSelected}`);
729    if (isSelected == (!this.inverseSelection)) {
730      this.toggleEntryItem(entry, targetId, true);
731    } else {
732      this.toggleEntryItem(entry, targetId, false);
733    }
734  }
735
736  private toggleEntryItem(entry: BucketSelectionEntry, targetId: string, isSelected: boolean): void {
737    Log.info(TAG, `toggleEntryItem ${targetId} ${isSelected}`);
738    let clickSet = entry.getClickSet();
739    if (isSelected != entry.getInverseSelection()) {
740      clickSet.add(targetId);
741    } else {
742      clickSet.delete(targetId);
743    }
744  }
745
746  private getCoordinateFromPosition(position: number): ItemCoordinate {
747    let index = 0;
748    let group = 0;
749    let totalSize = this.mGroupData.length;
750    for (; group < totalSize; group++) {
751      let count = this.mGroupData[group].count;
752      index += (count + 1);
753      if (index > position) {
754        index -= count;
755        group = Math.max(0, group);
756        break;
757      }
758    }
759    return new ItemCoordinate().setGroupId(group).setIndex(position - index);
760  }
761
762  private getGroupEntry(itemCoordinate: ItemCoordinate): BucketSelectionEntry {
763    let entry = this.mSelectionEntryArray[itemCoordinate.groupId];
764    if (entry == undefined) {
765      entry = new BucketSelectionEntry();
766      entry.setGroupId(itemCoordinate.groupId);
767      if (itemCoordinate.groupId >= 0 && itemCoordinate.groupId < this.mGroupData.length) {
768        Log.info(TAG, `entry.setTotalCount ${this.mGroupData[itemCoordinate.groupId].count}`);
769        entry.setTotalCount(this.mGroupData[itemCoordinate.groupId].count);
770      }
771      Log.info(TAG, `getGroupEntry mSelectionEntryArray ${itemCoordinate.groupId} entry: ${entry}`);
772      this.mSelectionEntryArray[itemCoordinate.groupId] = entry;
773    }
774    return entry;
775  }
776
777  private clearEntryArray(): void {
778    Log.info(TAG, 'clearEntryArray');
779    this.mSelectionEntryArray.length = 0;
780  }
781
782  private checkIsGetSelectionFinish(doneCount: number, result: string[], callback: AsyncCallback<string[]>): void {
783    if (this.mGroupData.length == doneCount) {
784      Log.info(TAG, `getSelection result ${result.length}`);
785      callback.callback(result);
786    }
787  }
788
789  private checkIsGetSelectionItemFinish(doneCount: number, result: MediaItem[], callback: Function): void {
790    if (this.mGroupData.length == doneCount) {
791      Log.info(TAG, `getSelection result ${result.length}`);
792      callback(result);
793    }
794  }
795
796  private async getSelectedFromEntry(entry: BucketSelectionEntry,
797                                     start: number, count: number, callback: Function): Promise<void> {
798    Log.info(TAG, `getSelectedFromEntry start: ${start}, count: ${count}`);
799    let result = new Array<string>();
800    if (entry.getInverseSelection()) {
801      await this.getItems(this.photoDataImpl, start, count, (temp: MediaItem[]) => {
802        temp.forEach((item) => {
803          if (!entry.getClickSet().has(item.uri)) {
804            result.push(item.uri);
805          }
806        });
807        callback(result);
808      });
809    } else {
810      Log.info(TAG, 'getSelectedFromEntry not inverse');
811      result = Array.from(entry.getClickSet());
812      callback(result);
813    }
814  }
815
816  private async getInverseSelectedFromEntry(entry: BucketSelectionEntry,
817                                            start: number, count: number, callback: Function): Promise<void> {
818    Log.info(TAG, `getInverseSelectedFromEntry start: ${start}, count: ${count}`);
819    let result = new Array<string>();
820    if (entry.getInverseSelection()) {
821      result = Array.from(entry.getClickSet());
822      callback(result);
823    } else {
824      Log.info(TAG, 'getInverseSelectedFromEntry not inverse');
825      await this.getItems(this.photoDataImpl, start, count, (temp: MediaItem[]) => {
826        Log.info(TAG, `enter callback temp: ${entry.getClickSet().size}`);
827        temp.forEach((item) => {
828          if (!entry.getClickSet().has(item.uri)) {
829            result.push(item.uri);
830          }
831        });
832        Log.info(TAG, `enter callback result ${result.length}`);
833        callback(result);
834      });
835    }
836  }
837}
838
839export class AlbumSetSelectManager extends SelectManager {
840  isDisableRenameClickedSet: Set<string> = new Set();
841  isDisableDeleteClickedSet: Set<string> = new Set();
842
843  constructor() {
844    super();
845  }
846
847  public toolBarStateToggle(targetId: string, isSelected: boolean,
848                            isDisableRename: boolean, isDisableDelete: boolean): void {
849    Log.info(TAG, `toolBarStateToggle${targetId}/${isSelected}/${isDisableRename}/${isDisableDelete}`);
850    if (isSelected == (!this.inverseSelection)) {
851      if (isDisableRename) {
852        Log.info(TAG, `add isDisableRename targetID ${targetId}`);
853        this.isDisableRenameClickedSet.add(targetId);
854      }
855      if (isDisableDelete) {
856        Log.info(TAG, `add isDisableDelete targetID ${targetId}`);
857        this.isDisableDeleteClickedSet.add(targetId);
858      }
859    } else {
860      if (isDisableRename) {
861        Log.info(TAG, `delete isDisableRename targetID ${targetId}`);
862        this.isDisableRenameClickedSet.delete(targetId);
863      }
864      if (isDisableDelete) {
865        Log.info(TAG, `delete isDisableDelete targetID ${targetId}`);
866        this.isDisableDeleteClickedSet.delete(targetId);
867      }
868    }
869
870    let isDisableRenameFlag = !(this.isDisableRenameClickedSet.size == 0);
871    let isDisableDeleteFlag = !(this.isDisableDeleteClickedSet.size == 0);
872    this.mCallbacks.has('updateToolBarState') &&
873    this.mCallbacks.get('updateToolBarState')(isDisableRenameFlag, isDisableDeleteFlag);
874  }
875
876  public onModeChange(newMode: boolean): void {
877    super.onModeChange(newMode);
878    if (!newMode) {
879      this.isDisableRenameClickedSet.clear();
880      this.isDisableDeleteClickedSet.clear();
881    }
882  }
883}