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