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 type { PhotoDataImpl } from './PhotoDataImpl'; 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, photoDataImpl?: PhotoDataImpl) { 106 super(); 107 this.photoDataImpl = photoDataImpl ?? BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO) as PhotoDataImpl; 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 getMediaCount(): number { 217 return this.mediaCount; 218 } 219 220 resetActiveWindow(): void { 221 this.activeStart = 0; 222 this.activeEnd = this.windowSize; 223 this.items = new Array<MediaItem>(this.windowSize); 224 } 225 226 // Initialize the first batch of data 227 initData(): void { 228 TraceControllerUtils.startTrace(this.initDataTraceName); 229 this.getItemCountFinish = false; 230 Log.info(TAG, `initData, ${this.getItemCountFinish}`); 231 this.pendingUpdateData = new PendingTask(<PendingCondition> { 232 shouldPending: () => { 233 return this.isPendingUpdateData; 234 } 235 }); 236 let start = 0; 237 let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT; 238 this.firstPatchDataRequestTime = Date.now(); 239 this.lastUpdateTime = this.firstPatchDataRequestTime; 240 let firstPatchDataCallback = { 241 callback: (assets: MediaItem[], dataAlbumUri?: string): void => { 242 Log.info(TAG, `took ${(Date.now() - this.firstPatchDataRequestTime)}\ 243 milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`); 244 if (this.isInvalidData(this.albumUri, dataAlbumUri)) { 245 Log.error(TAG, 'initData callback isInvalidData:this.albumUri:' + this.albumUri + ' dataAlbumUri:' + dataAlbumUri); 246 return; 247 } 248 if (assets?.length > 0) { 249 this.updateMediaData(this.firstPatchDataRequestTime, start, assets); 250 } 251 if (assets?.length < limit) { 252 return; 253 } 254 let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, limit); 255 let secondParam: QueryParam = { 256 albumUri: this.albumUri, 257 start: limit, 258 count: this.windowSize - limit, 259 }; 260 if (this.filterMediaType != undefined) { 261 secondParam.filterMediaType = this.filterMediaType; 262 } 263 this.photoDataImpl.getData(secondPatchDataCallback, secondParam); 264 } 265 }; 266 let firstParam: QueryParam = { 267 albumUri: this.albumUri, 268 start: start, 269 count: limit, 270 }; 271 if (this.filterMediaType != undefined) { 272 firstParam.filterMediaType = this.filterMediaType; 273 } 274 Log.info(TAG, `queryparam ${JSON.stringify(firstParam)}`); 275 this.photoDataImpl.getData(firstPatchDataCallback, firstParam); 276 this.loadData(); 277 } 278 279 // Query quantity 280 loadData(): void { 281 Log.info(TAG, `loadData, ${this.getItemCountFinish}`); 282 let initCountCallback: GetMediaCountCallback = new GetMediaCountCallback(this); 283 if (this.filterMediaType != undefined) { 284 this.photoDataImpl.getDataCount(initCountCallback, { 285 albumUri: this.albumUri, 286 filterMediaType: this.filterMediaType 287 }); 288 } else { 289 this.photoDataImpl.getDataCount(initCountCallback, { albumUri: this.albumUri }); 290 } 291 } 292 293 getItemIndexByUri(uri: string, indexNotifyCallback: Function): void { 294 Log.info(TAG, `getItemIndexByUri, ${uri}`); 295 let itemIndexCallback: GetItemIndexCallback = new GetItemIndexCallback(this, indexNotifyCallback); 296 if (this.filterMediaType) { 297 this.photoDataImpl.getDataIndexByUri(itemIndexCallback, { 298 albumUri: this.albumUri, 299 filterMediaType: this.filterMediaType 300 }, uri); 301 } else { 302 this.photoDataImpl.getDataIndexByUri(itemIndexCallback, { albumUri: this.albumUri }, uri); 303 } 304 } 305 306 // update media count 307 updateMediaCount(requestTime: number, count: number): void { 308 TraceControllerUtils.startTraceWithTaskId('updateMediaCount', requestTime); 309 Log.info(TAG, `updateMediaCount requestTime: ${requestTime}, count: ${count}, real size: ${this.realSize}`); 310 this.lastUpdateTime = requestTime; 311 312 this.getItemCountFinish = true; 313 this.addedCount = (this.realSize > Constants.NUMBER_0) ? (count - this.realSize) : Constants.NUMBER_0; 314 this.realSize = count; 315 this.updateSize(requestTime, count); 316 317 TraceControllerUtils.finishTraceWithTaskId('updateMediaCount', requestTime); 318 if (this.isPendingUpdateData) { 319 this.isPendingUpdateData = false; 320 this.pendingUpdateData.flush(); 321 } 322 } 323 324 /** 325 * Update related variables of count 326 * 327 * @param requestTime 328 * @param count 329 */ 330 updateSize(requestTime: number, count: number): void { 331 Log.info(TAG, `updateSize, old size: ${this.size}, new size: ${count}`) 332 this.isCountChanged = count != this.size; 333 this.isCountReduced = count < this.size; 334 this.mediaCount = count; 335 this.size = this.mediaCount; 336 this.dataIndexes = []; 337 this.layoutIndexes = []; 338 for (let i = 0; i < this.size; i++) { 339 this.dataIndexes.push(i); 340 this.layoutIndexes.push(i); 341 } 342 343 this.updateCountPostProcess(); 344 } 345 346 /** 347 * run after update count,Adjust sliding windows and query items as needed 348 */ 349 updateCountPostProcess(): void { 350 Log.info(TAG, 'updateCountPostProcess'); 351 // Exclude initData 352 if (this.hasNewChange) { 353 // when the total count less to activeEnd, the window need to change 354 if (this.activeEnd > this.mediaCount) { 355 let newActiveStart = Math.max(0, this.activeStart - (this.activeEnd - this.mediaCount)); 356 let newActiveEnd = newActiveStart + this.windowSize; 357 // data reuse 358 if (newActiveEnd > this.activeStart) { 359 this.dataReuse(newActiveStart, this.activeStart, newActiveEnd); 360 } 361 this.activeStart = newActiveStart; 362 this.activeEnd = newActiveEnd; 363 Log.info(TAG, `updateSlidingWindow, newActiveStart: 364 ${this.activeStart} , newActiveEnd:${this.activeEnd}`); 365 } 366 367 if (this.mediaCount == 0) { 368 this.hasNewChange = false; 369 this.onDataReloaded(); 370 } else if (this.isEditSaveReload || this.isCountChanged || this.isRefresh) { 371 // dirty data, need to get data again 372 Log.info(TAG, 'dirty data, need to get data again'); 373 let callback: GetItemsCallback = new GetItemsCallback(this, this.activeStart); 374 let param: QueryParam = { 375 albumUri: this.albumUri, 376 start: this.activeStart, 377 count: this.windowSize, 378 }; 379 if (this.filterMediaType != undefined) { 380 param.filterMediaType = this.filterMediaType; 381 } 382 this.photoDataImpl.getData(callback, param); 383 } else { 384 this.hasNewChange = false; 385 } 386 } 387 this.emitCountUpdateCallbacks(); 388 } 389 390 emitCountUpdateCallbacks(): void { 391 this.mCallbacks['updateCount'] && this.mCallbacks['updateCount'](this.mediaCount); 392 this.broadCast && this.broadCast.emit(Constants.ON_LOADING_FINISHED, [this.mediaCount]); 393 this.notifySizeLoadingFinished(this.mediaCount); 394 } 395 396 // Update data in data callback 397 updateMediaData(requestTime: number, start: number, mediaItems: MediaItem[]): void { 398 if (requestTime == this.firstPatchDataRequestTime && this.isPendingUpdateData && this.size == 0) { 399 Log.info(TAG, 'the callback of mediaItems is earlier than that of count.'); 400 this.updateCountThroughMediaItems(requestTime, mediaItems); 401 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 402 TraceControllerUtils.finishTrace(this.initDataTraceName); 403 } else if (this.isPendingUpdateData) { 404 Log.info(TAG, 'isPendingUpdateData execute start'); 405 this.pendingUpdateData.execute(() => { 406 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 407 TraceControllerUtils.finishTrace(this.initDataTraceName); 408 }); 409 } else { 410 this.mediaDataUpdateExecutor(requestTime, start, mediaItems); 411 TraceControllerUtils.finishTrace(this.initDataTraceName); 412 } 413 } 414 415 /** 416 * Update count through items 417 * 418 * @param requestTime 419 * @param mediaItems mediaItems 420 */ 421 updateCountThroughMediaItems(requestTime: number, mediaItems: MediaItem[]): void { 422 Log.info(TAG, 'updateCountThroughMediaItems'); 423 this.updateSize(0, mediaItems == null ? 0 : mediaItems.length); 424 } 425 426 /** 427 * Actual execution function of items update 428 * 429 * @param requestTime 430 * @param start items 431 * @param mediaItems mediaItems 432 */ 433 mediaDataUpdateExecutor(requestTime: number, start: number, mediaItems: MediaItem[]): void { 434 TraceControllerUtils.startTraceWithTaskId('updateMediaData', requestTime); 435 Log.info(TAG, `updateMediaData requestTime: ${requestTime}, start: ${start}, this.addedCount: ${this.addedCount}, this.isEditSaveReload: ${this.isEditSaveReload}`); 436 if (this.lastUpdateTime < this.lastChangeTime && this.isActive) { 437 Log.info(TAG, 'request data expired, request again!'); 438 this.loadData(); 439 } else { 440 this.hasNewChange = false; 441 } 442 443 if (mediaItems == undefined || mediaItems.length == 0) { 444 Log.error(TAG, 'results are empty!'); 445 return; 446 } 447 448 if (start >= this.activeEnd || start + mediaItems.length <= this.activeStart) { 449 Log.info(TAG, 'results are not in active window'); 450 return; 451 } 452 453 Log.info(TAG, `updateMediaData mediaItems.length: ${mediaItems.length}`); 454 let fromIndex = Math.max(start, this.activeStart); 455 let endIndex = Math.min(this.activeEnd, start + mediaItems.length); 456 this.validEndIndex = endIndex - 1; 457 Log.info(TAG, `updateMediaData listeners size ${this.listeners.length}`) 458 459 for (let i = fromIndex; i < endIndex; i++) { 460 this.items[i - this.activeStart] = mediaItems[i - start]; 461 Log.debug(TAG, `updateMediaData ${this.layoutIndexes[i]}, ${mediaItems[i - start].uri}, ${mediaItems[i - start].getTitle()}`); 462 } 463 464 if (this.isCountChanged || this.isRefresh) { 465 if (this.photosBroadCast && (this.isCountReduced || this.isRefresh || (this.addedCount > Constants.NUMBER_0)) && !this.isEditSaveReload) { 466 this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED, [this.addedCount]); 467 this.addedCount = Constants.NUMBER_0; 468 } else if (this.broadCast) { 469 this.broadCast.emit(BroadCastConstants.ON_DATA_RELOADED, []); 470 } else { 471 this.onDataReloaded(); 472 } 473 474 this.isCountChanged = false; 475 this.isCountReduced = false; 476 this.isRefresh = false; 477 } else { 478 for (let i = fromIndex; i < endIndex; i++) { 479 this.onDataChanged(this.layoutIndexes[i]); 480 } 481 } 482 483 if (this.isEditSaveReload) { 484 if (this.addedCount >= 0) { 485 this.photosBroadCast && this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED_WITH_EDIT, []); 486 this.addedCount = Constants.NUMBER_0; 487 } 488 } else { 489 this.notifyDataLoadingFinished(); 490 } 491 TraceControllerUtils.finishTraceWithTaskId('updateMediaData', requestTime); 492 } 493 494 enableGetData(flag: boolean): void { 495 this.enableGetDataFlag = flag; 496 } 497 498 // Update sliding window 499 public updateSlidingWindow(dataIndex: number): void { 500 if (dataIndex == Constants.INVALID || dataIndex == undefined) { 501 return; 502 } 503 let windowCenter = Math.round((this.activeStart + this.activeEnd) / 2); 504 if (Math.abs(dataIndex - windowCenter) < Constants.STEP) { 505 return; 506 } 507 if (dataIndex < windowCenter && this.activeStart == 0) { 508 return; 509 } 510 if (dataIndex > windowCenter && this.activeEnd >= this.mediaCount) { 511 return; 512 } 513 let newActiveStart = this.getWindowActiveStart(dataIndex, windowCenter); 514 let newActiveEnd = newActiveStart + this.windowSize; 515 let requestStart = newActiveStart; 516 let requestCount = this.windowSize; 517 Log.info(TAG, `dataIndex: ${dataIndex}, windowCenter: ${windowCenter}, newActiveStart=${newActiveStart}` 518 + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`); 519 520 if (newActiveEnd < this.activeStart || newActiveStart > this.activeEnd) { 521 let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT; 522 let start = Math.max(0, (newActiveStart + newActiveEnd) / 2 - limit / 2); 523 this.firstPatchDataRequestTime = Date.now(); 524 this.lastUpdateTime = this.firstPatchDataRequestTime; 525 let firstPatchDataCallback = { 526 callback: (assets: MediaItem[], dataAlbumUri?: string): void => { 527 Log.info(TAG, `took ${(Date.now() - this.firstPatchDataRequestTime)}\ 528 milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`); 529 if (this.isInvalidData(this.albumUri, dataAlbumUri)) { 530 Log.error(TAG, 'updateSlidingWindow callback isInvalidData:this.albumUri:' + this.getAlbumUri() + ' dataAlbumUri:' + dataAlbumUri); 531 return; 532 } 533 if (assets?.length > 0) { 534 this.updateMediaData(this.firstPatchDataRequestTime, start, assets); 535 } 536 if (assets?.length < limit) { 537 return; 538 } 539 let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, newActiveStart); 540 let secondParam: QueryParam = { 541 albumUri: this.albumUri, 542 start: newActiveStart, 543 count: this.windowSize 544 }; 545 if (this.filterMediaType != undefined) { 546 secondParam.filterMediaType = this.filterMediaType; 547 } 548 this.photoDataImpl.getData(secondPatchDataCallback, secondParam); 549 } 550 }; 551 let firstParam: QueryParam = { 552 albumUri: this.albumUri, 553 start: start, 554 count: limit 555 }; 556 if (this.filterMediaType != undefined) { 557 firstParam.filterMediaType = this.filterMediaType; 558 } 559 this.photoDataImpl.getData(firstPatchDataCallback, firstParam); 560 } 561 562 if (newActiveEnd < this.activeEnd && newActiveEnd > this.activeStart) { 563 requestCount = this.activeStart - newActiveStart; 564 this.dataReuse(newActiveStart, this.activeStart, newActiveEnd); 565 } 566 if (newActiveStart > this.activeStart && newActiveStart < this.activeEnd) { 567 requestStart = this.activeEnd; 568 requestCount = newActiveEnd - this.activeEnd; 569 this.dataReuse(newActiveStart, newActiveStart, this.activeEnd); 570 } 571 if (newActiveStart > this.activeEnd || newActiveEnd < this.activeStart) { 572 this.items = new Array(this.windowSize); 573 } 574 Log.info(TAG, `activeStart=${this.activeStart}, activeEnd=${this.activeEnd}, newActiveStart=${newActiveStart}` 575 + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`); 576 this.activeStart = newActiveStart; 577 this.activeEnd = newActiveEnd; 578 579 let callback: GetItemsCallback = new GetItemsCallback(this, requestStart); 580 let param: QueryParam = { 581 albumUri: this.albumUri, 582 start: requestStart, 583 count: requestCount 584 }; 585 if (this.filterMediaType != undefined) { 586 param.filterMediaType = this.filterMediaType; 587 } 588 this.photoDataImpl.getData(callback, param); 589 } 590 591 getMediaItemSafely(index: number): MediaItem { 592 let mediaItem: MediaItem = this.items[index]; 593 if (mediaItem == null) { 594 Log.error(TAG, `invalid data, index:${index}, active Start:${this.activeStart}, End:${this.activeEnd}`); 595 mediaItem = new MediaItem(null); 596 } 597 return mediaItem; 598 } 599 600 // Forced refresh interface 601 forceUpdate() { 602 this.onDataReloaded(); 603 } 604 605 // Packaging data for the view layer 606 getWrappedData(index: number): ViewData { 607 let mediaItemIndex: number = this.dataIndexes[index] - this.activeStart; 608 if (mediaItemIndex < 0 || mediaItemIndex >= this.items.length) { 609 return undefined; 610 } 611 let result = { 612 viewType: ViewType.ITEM, 613 mediaItem: this.getMediaItemSafely(mediaItemIndex), 614 viewIndex: index 615 }; 616 return result; 617 } 618 619 setAlbumUri(uri: string): void { 620 Log.info(TAG, `setAlbumUri: ${uri}`) 621 this.albumUri = uri; 622 } 623 624 getAlbumUri(): string { 625 return this.albumUri; 626 } 627 628 setFilterMediaType(filterMediaType: string): void { 629 Log.info(TAG, `set filterMediaType: ${filterMediaType}`) 630 this.filterMediaType = filterMediaType; 631 } 632 633 setBroadCast(broadCastParam: BroadCast): void { 634 this.broadCast = broadCastParam; 635 } 636 637 setPhotoBroadCast(broadCastParam: BroadCast): void { 638 this.photosBroadCast = broadCastParam; 639 } 640 641 releasePhotoBroadCast(): void { 642 this.photosBroadCast = null; 643 } 644 645 onPhotoBrowserActive(isActive: boolean, transition: string): void { 646 if (transition == Constants.PHOTO_TRANSITION_ALBUM || transition == Constants.PHOTO_TRANSITION_CAMERA) { 647 if (isActive) { 648 Log.info(TAG, 'onPhotoBrowserActive') 649 this.onActive(); 650 } else { 651 this.onInActive(); 652 } 653 } else if (transition == Constants.PHOTO_TRANSITION_EDIT) { 654 if (isActive) { 655 this.isEditSaveReload = true; 656 this.onActive(); 657 } else { 658 this.isEditSaveReload = false; 659 } 660 } 661 } 662 663 getIndexByMediaItem(item: MediaItem): number { 664 for (let index = 0; index < this.items.length; index++) { 665 if (item.uri == this.items[index].uri) { 666 this.items[index] = item 667 return index; 668 } 669 } 670 return -1; 671 } 672 673 // 父类方法需要子类覆写 674 getPositionByIndex(index: number): number { 675 return index; 676 } 677 678 onChange(mediaType: string): void { 679 if (mediaType === 'image' || mediaType === 'video' || mediaType === 'album') { 680 Log.debug(TAG, `local onChange ${mediaType}`); 681 super.onChange(mediaType); 682 } 683 } 684 685 switchRefreshOn(): void { 686 this.isRefresh = true 687 } 688 689 getGroupCountBeforeItem(item: MediaItem): number { 690 return 0; 691 } 692 693 private getWindowActiveStart(dataIndex: number, windowCenter: number): number { 694 let isForward = (dataIndex > windowCenter); 695 let halfWinSize = Math.round(this.windowSize / 2); 696 // The end of the window does not exceed the total amount of data when the window moves forward. 697 if (isForward) { 698 let forwardStep = dataIndex - windowCenter; 699 forwardStep = forwardStep % Constants.STEP == 0 700 ? forwardStep 701 : Math.ceil(forwardStep / Constants.STEP) * Constants.STEP; 702 let forwardStepSize = Math.min(forwardStep, Math.abs(this.mediaCount - this.activeEnd)); 703 Log.info(TAG, `forwardStep=${forwardStep}, stepSize=${forwardStepSize}, activeStart=${this.activeStart}`); 704 return (this.activeStart + forwardStepSize); 705 } else { 706 let backwardStep = windowCenter - dataIndex; 707 backwardStep = backwardStep % Constants.STEP == 0 708 ? backwardStep 709 : Math.ceil(backwardStep / Constants.STEP) * Constants.STEP; 710 Log.info(TAG, `backward step ${backwardStep}, activeStart=${this.activeStart}`); 711 return Math.max(0, this.activeStart - backwardStep); 712 } 713 } 714 715 /** 716 * data Reuse 717 * 718 * @param newActiveStart 719 * @param startIndex 720 * @param endIndex 721 */ 722 private dataReuse(newActiveStart: number, startIndex: number, endIndex: number): void { 723 let newData: MediaItem[] = new Array(this.windowSize); 724 for (let i = startIndex; i < endIndex; i++) { 725 newData[i - newActiveStart] = this.items[i - this.activeStart]; 726 } 727 this.items = newData; 728 } 729 730 isInvalidData(requestUri: string, dataUri: string): boolean { 731 if (dataUri === '' || dataUri === undefined || dataUri === null) { 732 return false; 733 } 734 return !(requestUri === dataUri); 735 } 736}