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}