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 { MediaItem } from './MediaItem'; 17import { ViewType } from './ViewType'; 18import { AbsDataSource } from '../AbsDataSource'; 19import { GetItemsCallback } from './GetItemsCallback'; 20import { GetMediaCountCallback } from './GetMediaCountCallback'; 21import { Log } from '../../../utils/Log'; 22import { BroadCastConstants } from '../../common/BroadCastConstants'; 23import { BroadCast } from '../../../utils/BroadCast'; 24import { BroadCastManager } from '../../common/BroadCastManager'; 25import { Constants } from '../../common/Constants'; 26import { PendingTask } from './PendingTask'; 27import type { PendingCondition } from './PendingCondition'; 28import { TraceControllerUtils } from '../../../utils/TraceControllerUtils'; 29import { AlbumDefine } from '../AlbumDefine'; 30import { BrowserDataFactory } from '../../../interface/BrowserDataFactory'; 31import type { BrowserDataInterface } from '../../../interface/BrowserDataInterface'; 32import type { QueryParam } from '../BrowserDataImpl'; 33import type { ViewData } from './ViewData'; 34import { GetItemIndexCallback } from './GetItemIndexCallback'; 35import { FileAsset } from '../../../access/UserFileManagerAccess'; 36import { GetItemCallback } from './GetItemCallback'; 37 38const TAG: string = 'common_MediaDataSource'; 39 40export class MediaDataSource extends AbsDataSource { 41 // Number of first pictures loaded during initialization 42 private static INIT_FIRST_PATCH_LOAD_COUNT = 50; 43 initDataTraceName: string = 'PhotoGridPageInitData'; 44 45 // Album list, all photos, etc. may involve album aggregation display, so multiple albums are required 46 photoDataImpl: BrowserDataInterface; 47 48 // Number of elements in layout 49 size: number = 0; 50 realSize: number = 0; 51 getItemCountFinish: boolean = false; 52 53 // Number of media in the dataset 54 mediaCount: number = 0; 55 56 // Is the quantity changed 57 isCountChanged: boolean = false; 58 59 // Is the quantity changed 60 isCountReduced: boolean = false; 61 62 addedCount: number = Constants.NUMBER_0; 63 64 // Save the amount of data of a window size 65 items: MediaItem[] = []; 66 67 // window Size 68 windowSize: number = 0; 69 70 // start point 71 activeStart: number = 0; 72 73 // end point 74 activeEnd: number = 0; 75 76 // layoutIndex to dataIndex 77 dataIndexes: number[] = []; 78 79 // dataIndex to layoutIndex 80 layoutIndexes: number[] = []; 81 broadCast: BroadCast; 82 photosBroadCast: BroadCast; 83 84 // The BroadCast of the application process. Event registration and destruction should be paired 85 appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast(); 86 87 // Whether to delay updating data 88 isPendingUpdateData: boolean = true; 89 90 // During initialization, the task to data updates before the delay when count returns 91 pendingUpdateData: PendingTask; 92 93 // Request time of the first batch of data 94 firstPatchDataRequestTime: number; 95 96 // 删除ID,通知统一使用uri,准确查询整机相册中的资源,图库再通过uri识别系统相册。 97 albumUri: string = undefined; 98 filterMediaType: string = undefined; 99 isRefresh: boolean = false; 100 isEditSaveReload: boolean = false; 101 validEndIndex: number; 102 private browserActiveCallback: Function = this.onPhotoBrowserActive.bind(this); 103 private enableGetDataFlag: boolean = true; 104 105 constructor(windowSize: number) { 106 super(); 107 this.photoDataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO); 108 this.windowSize = windowSize; 109 this.activeEnd = windowSize; 110 this.items = new Array(windowSize); 111 this.appBroadCast.on(BroadCastConstants.PHOTO_BROWSER_ACTIVE, this.browserActiveCallback); 112 } 113 114 releaseBroadCast(): void { 115 Log.debug(TAG, 'release all appBroadCast'); 116 this.appBroadCast.off(BroadCastConstants.PHOTO_BROWSER_ACTIVE, this.browserActiveCallback); 117 } 118 119 totalCount(): number { 120 if (this.lastTotalCount != this.size) { 121 Log.info(TAG, `totalCount: ${this.size}`); 122 this.lastTotalCount = this.size; 123 } 124 return this.size; 125 } 126 127 realTotalCount(): number { 128 Log.info(TAG, `realTotalCount: ${this.size}, ${this.getItemCountFinish}`); 129 if (this.getItemCountFinish == true) { 130 return this.size; 131 } 132 return -1; 133 } 134 135 getData(index: number): ViewData { 136 if (!this.enableGetDataFlag) { 137 return undefined; 138 } 139 this.updateSlidingWindow(this.dataIndexes[index]); 140 let result = this.getWrappedData(index); 141 if (result == null) { 142 return undefined; 143 } 144 return result; 145 } 146 147 // get raw MediaItem data 148 getRawData(dataIndex: number): MediaItem { 149 if (dataIndex < this.activeStart || dataIndex >= this.activeEnd) { 150 Log.warn(TAG, `dataIndex is invalid: ${dataIndex}, ${this.activeStart} ~ ${this.activeEnd}`); 151 return undefined; 152 } 153 return this.items[dataIndex - this.activeStart]; 154 } 155 156 getFirstRawData(): MediaItem { 157 if (this.items.length <= 0) { 158 return undefined; 159 } 160 return this.items[0]; 161 } 162 163 // get DataIndex with item 164 getDataIndex(item: MediaItem): number { 165 for (let i = 0; i < this.items.length; i++) { 166 if (this.items[i] != undefined && this.items[i].uri === item.uri) { 167 return i + this.activeStart; 168 } 169 } 170 return Constants.NOT_FOUND; 171 } 172 173 getItemByUri(uri: string): MediaItem { 174 for (let i = 0; i < this.items.length; i++) { 175 if (this.items[i] != undefined && this.items[i].uri == uri) { 176 return this.items[i]; 177 } 178 } 179 return null; 180 } 181 182 getDataIndexByUri(uri: string): number { 183 for (let i = 0; i < this.items.length; i++) { 184 if (this.items[i] != undefined && this.items[i].uri === uri) { 185 return i + this.activeStart; 186 } 187 } 188 return Constants.NOT_FOUND; 189 } 190 // Search for the index of first valid item in 500 items 191 getValidStartIndex(): number { 192 for (let i = 0; i < this.items.length; i++) { 193 if (this.items[i]) { 194 return i + this.activeStart; 195 } 196 } 197 return Constants.NOT_FOUND; 198 } 199 // Search for the index of last valid item in 500 items 200 getValidEndIndex(): number { 201 return this.validEndIndex; 202 } 203 204 getMediaItemByUri(uri: string): MediaItem { 205 if (this.items.length <= 0) { 206 return undefined; 207 } 208 for (let item of this.items) { 209 if (item != undefined && item.uri === uri) { 210 return item; 211 } 212 } 213 return undefined; 214 } 215 216 getMediaItemByUriFromAll(uri: string, itemNotifyCallback: Function): MediaItem { 217 Log.info(TAG, `getMediaItemByUriFromAll uri ${uri}`); 218 if (this.items.length <= 0) { 219 return undefined; 220 } 221 222 for (let item of this.items) { 223 if (item?.uri === uri) { 224 return item; 225 } 226 } 227 228 // 若当前数据不存在于当前列表的处理 229 Log.info(TAG, `getMediaItemByUriFromAll, ${uri}`); 230 let itemIndexCallback: GetItemCallback = new GetItemCallback(this, itemNotifyCallback); 231 this.photoDataImpl.getMediaItemByUri(itemIndexCallback, uri); 232 233 return undefined; 234 } 235 236 getMediaCount(): number { 237 return this.mediaCount; 238 } 239 240 resetActiveWindow(): void { 241 this.activeStart = 0; 242 this.activeEnd = this.windowSize; 243 this.items = new Array<MediaItem>(this.windowSize); 244 } 245 246 // Initialize the first batch of data 247 initData(): void { 248 TraceControllerUtils.startTrace(this.initDataTraceName); 249 this.getItemCountFinish = false; 250 Log.info(TAG, `initData, ${this.getItemCountFinish}`); 251 this.pendingUpdateData = new PendingTask(<PendingCondition> { 252 shouldPending: () => { 253 return this.isPendingUpdateData; 254 } 255 }); 256 let start = 0; 257 let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT; 258 this.firstPatchDataRequestTime = Date.now(); 259 this.lastUpdateTime = this.firstPatchDataRequestTime; 260 let firstPatchDataCallback = { 261 callback: (assets: MediaItem[]): void => { 262 Log.info(TAG, `took ${(Date.now() - this.firstPatchDataRequestTime)}\ 263 milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`); 264 if (assets.length > 0) { 265 this.updateMediaData(this.firstPatchDataRequestTime, start, assets); 266 let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, limit); 267 let secondParam: QueryParam = { 268 albumUri: this.albumUri, 269 start: limit, 270 count: this.windowSize - limit, 271 }; 272 if (this.filterMediaType != undefined) { 273 secondParam.filterMediaType = this.filterMediaType; 274 } 275 this.photoDataImpl.getData(secondPatchDataCallback, secondParam); 276 } 277 } 278 }; 279 let firstParam: QueryParam = { 280 albumUri: this.albumUri, 281 start: start, 282 count: limit, 283 }; 284 if (this.filterMediaType != undefined) { 285 firstParam.filterMediaType = this.filterMediaType; 286 } 287 Log.info(TAG, `queryparam ${JSON.stringify(firstParam)}`); 288 this.photoDataImpl.getData(firstPatchDataCallback, firstParam); 289 this.loadData(); 290 } 291 292 // Query quantity 293 loadData(): void { 294 Log.info(TAG, `loadData, ${this.getItemCountFinish}`); 295 let initCountCallback: GetMediaCountCallback = new GetMediaCountCallback(this); 296 if (this.filterMediaType != undefined) { 297 this.photoDataImpl.getDataCount(initCountCallback, { 298 albumUri: this.albumUri, 299 filterMediaType: this.filterMediaType 300 }); 301 } else { 302 this.photoDataImpl.getDataCount(initCountCallback, { albumUri: this.albumUri }); 303 } 304 } 305 306 getItemIndexByUri(uri: string, indexNotifyCallback: Function): void { 307 Log.info(TAG, `getItemIndexByUri, ${uri}`); 308 let itemIndexCallback: GetItemIndexCallback = new GetItemIndexCallback(this, indexNotifyCallback); 309 if (this.filterMediaType) { 310 this.photoDataImpl.getDataIndexByUri(itemIndexCallback, { 311 albumUri: this.albumUri, 312 filterMediaType: this.filterMediaType 313 }, uri); 314 } else { 315 this.photoDataImpl.getDataIndexByUri(itemIndexCallback, { albumUri: this.albumUri }, uri); 316 } 317 } 318 319 // update media count 320 updateMediaCount(requestTime: number, count: number): void { 321 TraceControllerUtils.startTraceWithTaskId('updateMediaCount', requestTime); 322 Log.info(TAG, `updateMediaCount requestTime: ${requestTime}, count: ${count}, real size: ${this.realSize}`); 323 this.lastUpdateTime = requestTime; 324 325 this.getItemCountFinish = true; 326 this.addedCount = (this.realSize > Constants.NUMBER_0) ? (count - this.realSize) : Constants.NUMBER_0; 327 this.realSize = count; 328 this.updateSize(requestTime, count); 329 330 TraceControllerUtils.finishTraceWithTaskId('updateMediaCount', requestTime); 331 if (this.isPendingUpdateData) { 332 this.isPendingUpdateData = false; 333 this.pendingUpdateData.flush(); 334 } 335 } 336 337 /** 338 * Update related variables of count 339 * 340 * @param requestTime 341 * @param count 342 */ 343 updateSize(requestTime: number, count: number): void { 344 Log.info(TAG, `updateSize, old size: ${this.size}, new size: ${count}`) 345 this.isCountChanged = count != this.size; 346 this.isCountReduced = count < this.size; 347 this.mediaCount = count; 348 this.size = this.mediaCount; 349 this.dataIndexes = []; 350 this.layoutIndexes = []; 351 for (let i = 0; i < this.size; i++) { 352 this.dataIndexes.push(i); 353 this.layoutIndexes.push(i); 354 } 355 356 this.updateCountPostProcess(); 357 } 358 359 /** 360 * run after update count,Adjust sliding windows and query items as needed 361 */ 362 updateCountPostProcess(): void { 363 Log.info(TAG, 'updateCountPostProcess'); 364 // Exclude initData 365 if (this.hasNewChange) { 366 // when the total count less to activeEnd, the window need to change 367 if (this.activeEnd > this.mediaCount) { 368 let newActiveStart = Math.max(0, this.activeStart - (this.activeEnd - this.mediaCount)); 369 let newActiveEnd = newActiveStart + this.windowSize; 370 // data reuse 371 if (newActiveEnd > this.activeStart) { 372 this.dataReuse(newActiveStart, this.activeStart, newActiveEnd); 373 } 374 this.activeStart = newActiveStart; 375 this.activeEnd = newActiveEnd; 376 Log.info(TAG, `updateSlidingWindow, newActiveStart: 377 ${this.activeStart} , newActiveEnd:${this.activeEnd}`); 378 } 379 380 if (this.mediaCount == 0) { 381 this.hasNewChange = false; 382 this.onDataReloaded(); 383 } else if (this.isEditSaveReload || this.isCountChanged || this.isRefresh) { 384 // dirty data, need to get data again 385 Log.info(TAG, 'dirty data, need to get data again'); 386 let callback: GetItemsCallback = new GetItemsCallback(this, this.activeStart); 387 let param: QueryParam = { 388 albumUri: this.albumUri, 389 start: this.activeStart, 390 count: this.windowSize, 391 }; 392 if (this.filterMediaType != undefined) { 393 param.filterMediaType = this.filterMediaType; 394 } 395 this.photoDataImpl.getData(callback, param); 396 } else { 397 this.hasNewChange = false; 398 } 399 } 400 this.emitCountUpdateCallbacks(); 401 } 402 403 emitCountUpdateCallbacks(): void { 404 this.mCallbacks['updateCount'] && this.mCallbacks['updateCount'](this.mediaCount); 405 this.broadCast && this.broadCast.emit(Constants.ON_LOADING_FINISHED, [this.mediaCount]); 406 this.notifySizeLoadingFinished(this.mediaCount); 407 } 408 409 // Update data in data callback 410 updateMediaData(requestTime: number, start: number, mediaItems: MediaItem[]): void { 411 if (requestTime == this.firstPatchDataRequestTime && this.isPendingUpdateData && this.size == 0) { 412 Log.info(TAG, 'the callback of mediaItems is earlier than that of count.'); 413 this.updateCountThroughMediaItems(requestTime, mediaItems); 414 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 415 TraceControllerUtils.finishTrace(this.initDataTraceName); 416 } else if (this.isPendingUpdateData) { 417 Log.info(TAG, 'isPendingUpdateData execute start'); 418 this.pendingUpdateData.execute(() => { 419 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 420 TraceControllerUtils.finishTrace(this.initDataTraceName); 421 }); 422 } else { 423 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 424 TraceControllerUtils.finishTrace(this.initDataTraceName); 425 } 426 } 427 428 /** 429 * Update count through items 430 * 431 * @param requestTime 432 * @param mediaItems mediaItems 433 */ 434 updateCountThroughMediaItems(requestTime: number, mediaItems: MediaItem[]): void { 435 Log.info(TAG, 'updateCountThroughMediaItems'); 436 this.updateSize(0, mediaItems == null ? 0 : mediaItems.length); 437 } 438 439 /** 440 * Actual execution function of items update 441 * 442 * @param requestTime 443 * @param start items 444 * @param mediaItems mediaItems 445 */ 446 mediaDataUpdateExecutor(requestTime: number, start: number, mediaItems: MediaItem[]): void { 447 TraceControllerUtils.startTraceWithTaskId('updateMediaData', requestTime); 448 Log.info(TAG, `updateMediaData requestTime: ${requestTime}, start: ${start}, this.addedCount: ${this.addedCount}, this.isEditSaveReload: ${this.isEditSaveReload}`); 449 if (this.lastUpdateTime < this.lastChangeTime && this.isActive) { 450 Log.info(TAG, 'request data expired, request again!'); 451 this.loadData(); 452 } else { 453 this.hasNewChange = false; 454 } 455 456 if (mediaItems == undefined || mediaItems.length == 0) { 457 Log.error(TAG, 'results are empty!'); 458 return; 459 } 460 461 if (start >= this.activeEnd || start + mediaItems.length <= this.activeStart) { 462 Log.info(TAG, 'results are not in active window'); 463 return; 464 } 465 466 Log.info(TAG, `updateMediaData mediaItems.length: ${mediaItems.length}`); 467 let fromIndex = Math.max(start, this.activeStart); 468 let endIndex = Math.min(this.activeEnd, start + mediaItems.length); 469 this.validEndIndex = endIndex - 1; 470 Log.info(TAG, `updateMediaData listeners size ${this.listeners.length}`) 471 472 for (let i = fromIndex; i < endIndex; i++) { 473 this.items[i - this.activeStart] = mediaItems[i - start]; 474 Log.debug(TAG, `updateMediaData ${this.layoutIndexes[i]}, ${mediaItems[i - start].uri}, ${mediaItems[i - start].getTitle()}`); 475 } 476 477 if (this.isCountChanged || this.isRefresh) { 478 if (this.photosBroadCast && (this.isCountReduced || this.isRefresh || (this.addedCount > Constants.NUMBER_0)) && !this.isEditSaveReload) { 479 this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED, [this.addedCount]); 480 this.addedCount = Constants.NUMBER_0; 481 } else if (this.broadCast) { 482 this.broadCast.emit(BroadCastConstants.ON_DATA_RELOADED, []); 483 } else { 484 this.onDataReloaded(); 485 } 486 487 this.isCountChanged = false; 488 this.isCountReduced = false; 489 this.isRefresh = false; 490 } else { 491 for (let i = fromIndex; i < endIndex; i++) { 492 this.onDataChanged(this.layoutIndexes[i]); 493 } 494 } 495 496 if (this.isEditSaveReload) { 497 if (this.addedCount >= 0) { 498 this.photosBroadCast && this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED_WITH_EDIT, []); 499 this.addedCount = Constants.NUMBER_0; 500 } 501 } else { 502 this.notifyDataLoadingFinished(); 503 } 504 TraceControllerUtils.finishTraceWithTaskId('updateMediaData', requestTime); 505 } 506 507 enableGetData(flag: boolean): void { 508 this.enableGetDataFlag = flag; 509 } 510 511 // Update sliding window 512 public updateSlidingWindow(dataIndex: number): void { 513 if (dataIndex == Constants.INVALID || dataIndex == undefined) { 514 return; 515 } 516 let windowCenter = Math.round((this.activeStart + this.activeEnd) / 2); 517 if (Math.abs(dataIndex - windowCenter) < Constants.STEP) { 518 return; 519 } 520 if (dataIndex < windowCenter && this.activeStart == 0) { 521 return; 522 } 523 if (dataIndex > windowCenter && this.activeEnd >= this.mediaCount) { 524 return; 525 } 526 let newActiveStart = this.getWindowActiveStart(dataIndex, windowCenter); 527 let newActiveEnd = newActiveStart + this.windowSize; 528 let requestStart = newActiveStart; 529 let requestCount = this.windowSize; 530 Log.info(TAG, `dataIndex: ${dataIndex}, windowCenter: ${windowCenter}, newActiveStart=${newActiveStart}` 531 + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`); 532 533 if (newActiveEnd < this.activeStart || newActiveStart > this.activeEnd) { 534 let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT; 535 let start = Math.max(0, (newActiveStart + newActiveEnd) / 2 - limit / 2); 536 this.firstPatchDataRequestTime = Date.now(); 537 this.lastUpdateTime = this.firstPatchDataRequestTime; 538 let firstPatchDataCallback = { 539 callback: (assets: MediaItem[]): void => { 540 Log.info(TAG, `took ${(Date.now() - this.firstPatchDataRequestTime)}\ 541 milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`); 542 if (assets.length > 0) { 543 this.updateMediaData(this.firstPatchDataRequestTime, start, assets); 544 let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, newActiveStart); 545 let secondParam: QueryParam = { 546 albumUri: this.albumUri, 547 start: newActiveStart, 548 count: this.windowSize 549 }; 550 if (this.filterMediaType != undefined) { 551 secondParam.filterMediaType = this.filterMediaType; 552 } 553 this.photoDataImpl.getData(secondPatchDataCallback, secondParam); 554 } 555 } 556 }; 557 let firstParam: QueryParam = { 558 albumUri: this.albumUri, 559 start: start, 560 count: limit 561 }; 562 if (this.filterMediaType != undefined) { 563 firstParam.filterMediaType = this.filterMediaType; 564 } 565 this.photoDataImpl.getData(firstPatchDataCallback, firstParam); 566 } 567 568 if (newActiveEnd < this.activeEnd && newActiveEnd > this.activeStart) { 569 requestCount = this.activeStart - newActiveStart; 570 this.dataReuse(newActiveStart, this.activeStart, newActiveEnd); 571 } 572 if (newActiveStart > this.activeStart && newActiveStart < this.activeEnd) { 573 requestStart = this.activeEnd; 574 requestCount = newActiveEnd - this.activeEnd; 575 this.dataReuse(newActiveStart, newActiveStart, this.activeEnd); 576 } 577 if (newActiveStart > this.activeEnd || newActiveEnd < this.activeStart) { 578 this.items = new Array(this.windowSize); 579 } 580 Log.info(TAG, `activeStart=${this.activeStart}, activeEnd=${this.activeEnd}, newActiveStart=${newActiveStart}` 581 + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`); 582 this.activeStart = newActiveStart; 583 this.activeEnd = newActiveEnd; 584 585 let callback: GetItemsCallback = new GetItemsCallback(this, requestStart); 586 let param: QueryParam = { 587 albumUri: this.albumUri, 588 start: requestStart, 589 count: requestCount 590 }; 591 if (this.filterMediaType != undefined) { 592 param.filterMediaType = this.filterMediaType; 593 } 594 this.photoDataImpl.getData(callback, param); 595 } 596 597 getMediaItemSafely(index: number): MediaItem { 598 let mediaItem: MediaItem = this.items[index]; 599 if (mediaItem == null) { 600 Log.error(TAG, `invalid data, index:${index}, active Start:${this.activeStart}, End:${this.activeEnd}`); 601 mediaItem = new MediaItem(null); 602 } 603 return mediaItem; 604 } 605 606 // Forced refresh interface 607 forceUpdate() { 608 this.onDataReloaded(); 609 } 610 611 // Packaging data for the view layer 612 getWrappedData(index: number): ViewData { 613 let mediaItemIndex: number = this.dataIndexes[index] - this.activeStart; 614 if (mediaItemIndex < 0 || mediaItemIndex >= this.items.length) { 615 return undefined; 616 } 617 let result = { 618 viewType: ViewType.ITEM, 619 mediaItem: this.getMediaItemSafely(mediaItemIndex), 620 viewIndex: index 621 }; 622 return result; 623 } 624 625 setAlbumUri(uri: string): void { 626 Log.info(TAG, `setAlbumUri: ${uri}`) 627 this.albumUri = uri; 628 } 629 630 setFilterMediaType(filterMediaType: string): void { 631 Log.info(TAG, `set filterMediaType: ${filterMediaType}`) 632 this.filterMediaType = filterMediaType; 633 } 634 635 setBroadCast(broadCastParam: BroadCast): void { 636 this.broadCast = broadCastParam; 637 } 638 639 setPhotoBroadCast(broadCastParam: BroadCast): void { 640 this.photosBroadCast = broadCastParam; 641 } 642 643 releasePhotoBroadCast(): void { 644 this.photosBroadCast = null; 645 } 646 647 onPhotoBrowserActive(isActive: boolean, transition: string): void { 648 if (transition == Constants.PHOTO_TRANSITION_ALBUM || transition == Constants.PHOTO_TRANSITION_CAMERA) { 649 if (isActive) { 650 Log.info(TAG, 'onPhotoBrowserActive') 651 this.onActive(); 652 } else { 653 this.onInActive(); 654 } 655 } else if (transition == Constants.PHOTO_TRANSITION_EDIT) { 656 if (isActive) { 657 this.isEditSaveReload = true; 658 this.onActive(); 659 } else { 660 this.isEditSaveReload = false; 661 } 662 } 663 } 664 665 getIndexByMediaItem(item: MediaItem): number { 666 for (let index = 0; index < this.items.length; index++) { 667 if (item.uri == this.items[index].uri) { 668 this.items[index] = item 669 return index; 670 } 671 } 672 return -1; 673 } 674 675 // 父类方法需要子类覆写 676 getPositionByIndex(index: number): number { 677 return index; 678 } 679 680 onChange(mediaType: string): void { 681 if (mediaType === 'image' || mediaType === 'video' || mediaType === 'album') { 682 Log.debug(TAG, `local onChange ${mediaType}`); 683 super.onChange(mediaType); 684 } 685 } 686 687 switchRefreshOn(): void { 688 this.isRefresh = true 689 } 690 691 getGroupCountBeforeItem(item: MediaItem): number { 692 return 0; 693 } 694 695 private getWindowActiveStart(dataIndex: number, windowCenter: number): number { 696 let isForward = (dataIndex > windowCenter); 697 let halfWinSize = Math.round(this.windowSize / 2); 698 // The end of the window does not exceed the total amount of data when the window moves forward. 699 if (isForward) { 700 let forwardStep = dataIndex - windowCenter; 701 forwardStep = forwardStep % Constants.STEP == 0 702 ? forwardStep 703 : Math.ceil(forwardStep / Constants.STEP) * Constants.STEP; 704 let forwardStepSize = Math.min(forwardStep, Math.abs(this.mediaCount - this.activeEnd)); 705 Log.info(TAG, `forwardStep=${forwardStep}, stepSize=${forwardStepSize}, activeStart=${this.activeStart}`); 706 return (this.activeStart + forwardStepSize); 707 } else { 708 let backwardStep = windowCenter - dataIndex; 709 backwardStep = backwardStep % Constants.STEP == 0 710 ? backwardStep 711 : Math.ceil(backwardStep / Constants.STEP) * Constants.STEP; 712 Log.info(TAG, `backward step ${backwardStep}, activeStart=${this.activeStart}`); 713 return Math.max(0, this.activeStart - backwardStep); 714 } 715 } 716 717 /** 718 * data Reuse 719 * 720 * @param newActiveStart 721 * @param startIndex 722 * @param endIndex 723 */ 724 private dataReuse(newActiveStart: number, startIndex: number, endIndex: number): void { 725 let newData: MediaItem[] = new Array(this.windowSize); 726 for (let i = startIndex; i < endIndex; i++) { 727 newData[i - newActiveStart] = this.items[i - this.activeStart]; 728 } 729 this.items = newData; 730 } 731}