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