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}