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 { Log } from '../../utils/Log'; 17import type { LoadingListener } from './LoadingListener'; 18 19const TAG: string = 'common_AbsDataSource'; 20 21// Abs DataSource 22export abstract class AbsDataSource implements IDataSource { 23 // Last data change time 24 lastChangeTime = 0; 25 26 // Last refresh time 27 lastUpdateTime = 0; 28 lastTotalCount = -1; 29 30 // Data change monitoring 31 listeners: DataChangeListener[] = []; 32 33 // callbacks 34 mCallbacks = new Map<string, Function>(); 35 36 // Is data initialized 37 isInitData: boolean; 38 39 // Are there any new data changes 40 hasNewChange = false; 41 42 // Freeze data refresh 43 isFreezeDataUpdate = false; 44 45 // Whether the page is in the foreground, and the data can be refreshed in the foreground 46 isActive = true; 47 48 // Data loading listener 49 private loadingListeners: Array<LoadingListener> = new Array<LoadingListener>(); 50 51 constructor() { 52 this.registerObserver(); 53 } 54 55 abstract initData(): void; 56 57 abstract loadData(): void; 58 59 abstract totalCount(): number; 60 61 abstract getData(index: number): any; 62 63 initialize(): void { 64 if (!this.isInitData) { 65 this.initData(); 66 this.isInitData = true; 67 } 68 } 69 70 registerDataChangeListener(listener: DataChangeListener): void { 71 Log.info(TAG, 'registerDataChangeListener'); 72 if (this.listeners.indexOf(listener) < 0) { 73 this.listeners.push(listener); 74 Log.info(TAG, `registerDataChangeListener, add listener, length: ${this.listeners.length}`); 75 } 76 this.initialize(); 77 } 78 79 unregisterDataChangeListener(listener: DataChangeListener): void { 80 Log.info(TAG, 'unregisterDataChangeListener'); 81 const pos = this.listeners.indexOf(listener); 82 if (pos >= 0) { 83 this.listeners.splice(pos, 1); 84 Log.info(TAG, `registerDataChangeListener, remove listener, length: ${this.listeners.length}`); 85 } 86 this.unregisterObserver(); 87 } 88 89 /** 90 * Overall refresh of notification framework 91 */ 92 onDataReloaded(): void { 93 if (this.isFreezeDataUpdate) { 94 return; 95 } 96 Log.info(TAG, `onDataReloaded listeners size ${this.listeners.length}`) 97 this.listeners.forEach(listener => { 98 listener.onDataReloaded(); 99 }) 100 } 101 102 /** 103 * Notification frame refresh by index 104 * 105 * @param layoutIndex index 106 */ 107 onDataChanged(layoutIndex: number): void { 108 if (this.isFreezeDataUpdate) { 109 return; 110 } 111 this.listeners.forEach(listener => { 112 listener.onDataChanged(layoutIndex); 113 }) 114 } 115 116 /** 117 * Delete frame refresh by index 118 * 119 * @param layoutIndex index 120 */ 121 onDataDeleted(layoutIndex: number): void { 122 if (this.isFreezeDataUpdate) { 123 return; 124 } 125 this.listeners.forEach(listener => { 126 listener.onDataDeleted(layoutIndex); 127 }) 128 } 129 130 public registerObserver(): void { 131 } 132 133 public unregisterObserver(): void { 134 } 135 136 registerCallback(name: string, cb: Function) { 137 this.mCallbacks[name] = cb; 138 } 139 140 unregisterCallback(name) { 141 this.mCallbacks[name] = undefined; 142 } 143 144 addLoadingListener(listener: LoadingListener): void { 145 if (listener == null) { 146 Log.error(TAG, 'listener is null'); 147 return; 148 } 149 if (this.loadingListeners.indexOf(listener) > -1) { 150 return; 151 } 152 this.loadingListeners.push(listener); 153 } 154 155 removeLoadingListener(listener: LoadingListener): void { 156 if (listener == null) { 157 Log.error(TAG, 'listener is null'); 158 return; 159 } 160 let index = this.loadingListeners.indexOf(listener); 161 if (index > -1) { 162 this.loadingListeners.splice(index, 1); 163 } 164 } 165 166 notifyDataChanged(dataIndex: number): void { 167 Log.debug(TAG, `notifyDataChanged,loadingListeners size:${this.loadingListeners.length},index:${dataIndex}`); 168 for (let listener of this.loadingListeners) { 169 listener.onDataChanged(dataIndex); 170 } 171 } 172 173 notifyDataLoadingFinished(): void { 174 Log.info(TAG, `notifyDataLoadingFinished, loadingListeners size:${this.loadingListeners.length}`); 175 for (let listener of this.loadingListeners) { 176 listener.onDataLoadingFinished(); 177 } 178 } 179 180 notifySizeLoadingFinished(size: number): void { 181 Log.info(TAG, `notifySizeLoadingFinished, loadingListeners size: ${this.loadingListeners.length}`); 182 for (let listener of this.loadingListeners) { 183 listener.onSizeLoadingFinished(size); 184 } 185 } 186 187 onActive(): void { 188 Log.info(TAG, `onActive, lastUpdateTime=${this.lastUpdateTime}, lastChangeTime=${this.lastChangeTime}`); 189 if (this.isActive === false) { 190 this.isActive = true; 191 if (this.lastUpdateTime < this.lastChangeTime) { 192 // Page back to the foreground, if there is a refresh media library reload refresh. 193 this.loadData(); 194 } 195 } 196 } 197 198 onInActive(): void { 199 Log.info(TAG, 'onInActive'); 200 this.isActive = false; 201 } 202 203 freeze(): void { 204 Log.info(TAG, 'freeze') 205 this.isFreezeDataUpdate = true; 206 } 207 208 unfreeze(): void { 209 Log.info(TAG, 'unfreeze') 210 this.isFreezeDataUpdate = false; 211 } 212 213 onChange(mediaType) { 214 this.lastChangeTime = Date.now(); 215 Log.debug(TAG, `onChange mediaType: ${mediaType} ${this.hasNewChange} ${this.isActive}`); 216 if (!this.hasNewChange) { 217 this.hasNewChange = true; 218 if (this.isActive) { 219 this.loadData(); 220 } 221 } 222 } 223}