• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2022 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 dataShare from '@ohos.data.dataShare';
17import dataSharePredicates from "@ohos.data.dataSharePredicates";
18import IContactRepository from './IContactRepository';
19import Contact from '../entity/Contact';
20import RawContact from '../entity/RawContact';
21import ContactBuilder from '../entity/ContactBuilder';
22import { DataItem } from '../entity/DataItem';
23import ContactList from './ContactList';
24import DAOperation from './DAOperation';
25import ContactDelta from './ContactDelta';
26import { RawContacts } from '../contract/RawContacts';
27import { Contacts } from '../contract/Contacts';
28import { Data } from '../contract/Data';
29import { HiLog } from '../../../../../../common/src/main/ets/util/HiLog';
30import ContactListItem from './ContactListItem';
31import { DataItemType } from '../contract/DataType';
32
33const TAG = "ContactRepository";
34
35/**
36 * Contact storage management, shielding dependency on the CP layer
37 * Contacts Only
38 */
39export class ContactRepository implements IContactRepository {
40  static readonly RAW_CONTACT_URL: string = RawContacts.CONTENT_URI;
41  private dataShareHelper;
42  private static instance: ContactRepository;
43  private context: Context
44
45  private constructor() {
46  }
47
48  /*
49   * init if Call From serviceAbility globalThis.context is Null
50   *@param ctx Context used for dataShare
51   */
52  init(ctx: Context) {
53    this.context = ctx;
54  }
55
56  public static getInstance(): ContactRepository {
57    if (!ContactRepository.instance) {
58      ContactRepository.instance = new ContactRepository();
59    }
60    return ContactRepository.instance;
61  }
62
63  saveTest() {
64    return false;
65  }
66
67  private async getDataAbilityHelper() {
68    if (this.dataShareHelper == undefined) {
69      this.dataShareHelper = await dataShare.createDataShareHelper(this.context ? this.context : globalThis.context,
70          Contacts.CONTENT_URI);
71    }
72    return this.dataShareHelper;
73  }
74
75  save(contact: ContactDelta, callback) {
76    this.getDataAbilityHelper().then((dataAbilityHelper) => {
77      let opts = contact.buildDiff();
78      return dataAbilityHelper.executeBatch(ContactRepository.RAW_CONTACT_URL, opts)
79        .then(resultSet => {
80          callback(resultSet);
81        })
82        .catch(error => {
83          HiLog.w(TAG, 'save error:%s' + JSON.stringify(error.message));
84          callback();
85        });
86    }).catch(error => {
87      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
88      callback();
89    });
90  }
91
92  findById(id: number, callback) {
93    if (id < 0) {
94      HiLog.w(TAG, 'findById: id is invalid.');
95      callback();
96      return;
97    }
98    this.getDataAbilityHelper().then((dataAbilityHelper) => {
99      let conditionArgs = new dataSharePredicates.DataSharePredicates();
100      conditionArgs.equalTo(RawContacts.CONTACT_ID, id).orderByAsc(Data.RAW_CONTACT_ID);
101      dataAbilityHelper.query(Data.CONTENT_URI, conditionArgs, null).then(resultSet => {
102        if (resultSet == undefined || !resultSet.goToFirstRow()) {
103          HiLog.w(TAG, 'findById not found.');
104          callback();
105          return;
106        }
107        let contactBuilder = ContactBuilder.fromResultSet(resultSet);
108        let currentRawContactId = -1;
109        let rawContact: RawContact = null;
110        do {
111          let rawContactId = resultSet.getLong(resultSet.getColumnIndex(Data.RAW_CONTACT_ID));
112          if (rawContactId != currentRawContactId) {
113            currentRawContactId = rawContactId;
114            rawContact = RawContact.fromResultSet(resultSet);
115            contactBuilder.rowContacts.push(rawContact);
116          }
117          if (rawContact != undefined) {
118            rawContact.dataItems.push(DataItem.fromResultSet(resultSet));
119          }
120        } while (resultSet.goToNextRow());
121        resultSet.close();
122        callback(contactBuilder.buildContact());
123      }).catch(error => {
124        HiLog.e(TAG, 'findById error:%s' + JSON.stringify(error.message));
125        callback();
126      });
127    }).catch(error => {
128      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
129      callback();
130    });
131  }
132
133  findAll(actionData, callback) {
134    let conditionArgs = new dataSharePredicates.DataSharePredicates();
135    let offset = actionData.page < 3 ? (actionData.page - 1) * 50 : (actionData.page - 2) * 500 + 50;
136    conditionArgs.limit(actionData.limit, offset)
137    //      conditionArgs.limitAs(actionData.limit);
138    //      conditionArgs.offsetAs(offset);
139    conditionArgs.equalTo(RawContacts.IS_DELETED, '0').orderByAsc(RawContacts.SORT_FIRST_LETTER);
140    this.getDataAbilityHelper().then((dataAbilityHelper) => {
141      dataAbilityHelper.query(Contacts.CONTACT_URI, conditionArgs, ContactListItem.COLUMNS)
142        .then(resultSet => {
143          let rst: ContactListItem[] = [];
144          if (resultSet.rowCount === 0) {
145            resultSet.close();
146            callback(rst);
147          } else {
148            resultSet.goToFirstRow();
149            do {
150              rst.push(new ContactListItem(resultSet));
151            } while (resultSet.goToNextRow());
152            resultSet.close();
153            callback(rst);
154          }
155        })
156        .catch(error => {
157          HiLog.w(TAG, 'findAll error:%s' + JSON.stringify(error.message));
158          callback([]);
159        });
160    }).catch(error => {
161      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
162      callback([]);
163    });
164  }
165
166  findByQuickSearchKey(searchKey: string, callback) {
167    this.getDataAbilityHelper().then((dataAbilityHelper) => {
168      let conditionArgs = new dataSharePredicates.DataSharePredicates();
169      conditionArgs.equalTo(Contacts.QUICK_SEARCH_KEY, searchKey).orderByAsc(Data.RAW_CONTACT_ID);
170      dataAbilityHelper.query(Data.CONTENT_URI, conditionArgs, null).then(resultSet => {
171        if (resultSet == undefined || !resultSet.goToFirstRow()) {
172          HiLog.w(TAG, 'findByQuickSearchKey not found.');
173          callback();
174          return;
175        }
176        let contactBuilder = ContactBuilder.fromResultSet(resultSet);
177        let currentRawContactId = -1;
178        let rawContact: RawContact = null;
179        do {
180          let rawContactId = resultSet.getLong(resultSet.getColumnIndex(Data.RAW_CONTACT_ID));
181          if (rawContactId != currentRawContactId) {
182            currentRawContactId = rawContactId;
183            rawContact = RawContact.fromResultSet(resultSet);
184            contactBuilder.rowContacts.push(rawContact);
185          }
186          if (rawContact != undefined) {
187            rawContact.dataItems.push(DataItem.fromResultSet(resultSet));
188          }
189        } while (resultSet.goToNextRow());
190        resultSet.close();
191        callback(contactBuilder.buildContact());
192      }).catch(error => {
193        HiLog.e(TAG, 'findByQuickSearchKey error:%s' + JSON.stringify(error.message));
194        callback();
195      });
196    }).catch(error => {
197      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
198      callback();
199    });
200  }
201
202  findAllWithBookIndex() {
203    return new ContactList({});
204  }
205
206  search(queryStr: string) {
207    return new ContactList({});
208  }
209
210  findByPhoneIsNotNull(callback) {
211    this.getAllContactNumbers((contactNumberMap) => {
212      this.getDataAbilityHelper().then((dataAbilityHelper) => {
213        let conditionArgs = new dataSharePredicates.DataSharePredicates();
214        conditionArgs.equalTo(RawContacts.IS_DELETED, '0')
215          .and()
216          .equalTo(Contacts.HAS_PHONE_NUMBER, '1')
217          .orderByAsc(RawContacts.SORT_FIRST_LETTER);
218        dataAbilityHelper.query(Contacts.CONTACT_URI, conditionArgs, ContactListItem.COLUMNS)
219          .then(resultSet => {
220            let rst: ContactListItem[] = [];
221            if (resultSet.rowCount === 0) {
222              resultSet.close();
223              callback(rst);
224            } else {
225              resultSet.goToFirstRow();
226              do {
227                let id = resultSet.getLong(resultSet.getColumnIndex(Contacts.ID));
228                if (!contactNumberMap.has(id)) {
229                  HiLog.w(TAG, 'findAll: contact id is invalid or contact has no phone number.');
230                  continue;
231                }
232                let contactListItem = new ContactListItem(resultSet);
233                contactListItem.phoneNumbers = contactNumberMap.get(id);
234                rst.push(contactListItem);
235              } while (resultSet.goToNextRow());
236              resultSet.close();
237              callback(rst);
238            }
239          })
240          .catch(error => {
241            HiLog.w(TAG, 'findAll error:%s' + JSON.stringify(error.message));
242            callback();
243          });
244      }).catch(error => {
245        HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
246        callback();
247      });
248    });
249  }
250
251  /**
252   * 查询所有联系人手机号
253   */
254  private getAllContactNumbers(callback) {
255    this.getDataAbilityHelper().then((dataAbilityHelper) => {
256      let resultColumns = [RawContacts.CONTACT_ID, Data.DETAIL_INFO, Data.EXTEND7, Data.CUSTOM_DATA];
257      let conditionArgs = new dataSharePredicates.DataSharePredicates();
258      conditionArgs.equalTo(Data.TYPE_ID, DataItemType.PHONE).orderByAsc(RawContacts.CONTACT_ID);
259      dataAbilityHelper.query(Data.CONTENT_URI, conditionArgs, resultColumns).then(resultSet => {
260        // 用于存储联系人及其电话号码的对应关系
261        let contactNumberMap = new Map();
262        if (resultSet == undefined || !resultSet.goToFirstRow()) {
263          HiLog.w(TAG, 'getAllContactNumbers not found.');
264          callback(contactNumberMap);
265          return;
266        }
267        let oldContact = resultSet.getLong(resultSet.getColumnIndex(RawContacts.CONTACT_ID));
268        let oldPhoneNumber = "";
269        let numberList = [];
270        do {
271          let newContact = resultSet.getLong(resultSet.getColumnIndex(RawContacts.CONTACT_ID));
272          let phoneNumberObj = {
273            'phoneNumber': resultSet.getString(resultSet.getColumnIndex(Data.DETAIL_INFO)),
274            'labelId': resultSet.getString(resultSet.getColumnIndex(Data.EXTEND7)),
275            'numType': resultSet.getString(resultSet.getColumnIndex(Data.CUSTOM_DATA))
276          };
277          // 如果是同一联系人则把手机号放到同一个list中
278          if (oldContact === newContact) {
279            if (oldPhoneNumber != phoneNumberObj.phoneNumber) {
280              numberList.push(phoneNumberObj);
281            }
282          } else {
283            // 联系人变化时,存储联系人与手机号码列表的对应关系
284            contactNumberMap.set(oldContact, numberList);
285            oldContact = newContact;
286            // 将最新的号码数据存储到新的numberList
287            numberList = [phoneNumberObj];
288          }
289          oldPhoneNumber = phoneNumberObj.phoneNumber;
290        } while (resultSet.goToNextRow());
291        contactNumberMap.set(oldContact, numberList);
292        resultSet.close();
293        callback(contactNumberMap);
294      });
295    }).catch(error => {
296      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
297      callback(new Map());
298    });
299  }
300
301  findByMailIsNotNull() {
302    return new ContactList({});
303  }
304
305  deleteById(id: number, callback) {
306    if (id < 0) {
307      callback();
308      return;
309    }
310    this.getDataAbilityHelper().then((dataAbilityHelper) => {
311      let condition = new dataSharePredicates.DataSharePredicates();
312      condition.equalTo(Contacts.ID, id);
313      dataAbilityHelper.delete(Contacts.CONTACT_URI, condition).then(data => {
314        callback(data);
315      }).catch(error => {
316        HiLog.w(TAG, 'deleteById error:%s' + JSON.stringify(error.message));
317        callback();
318      });
319    }).catch(error => {
320      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
321      callback();
322    });
323  }
324
325  deleteByIdIn(ids: number[]) {
326    return false;
327  }
328
329  notifyChange() {
330    this.getDataAbilityHelper().then((dataAbilityHelper) => {
331      if (dataAbilityHelper) {
332        dataAbilityHelper.notifyChange(Data.CONTENT_URI).then(() => {
333          HiLog.i(TAG, "notifyChange success")
334        }).catch(error => {
335          HiLog.w(TAG, 'notifyChange error:%s' + JSON.stringify(error.message));
336        });
337      }
338    }).catch(error => {
339      HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
340    });
341  }
342
343  observers:Array<()=>void> = [];
344
345  callback = ()=>{
346    HiLog.d(TAG, 'Contacts changed: Notifying observers...');
347    for (const observer of this.observers) {
348      observer();
349    }
350  }
351
352  registerDataChangeObserver(observer:()=>void) {
353    if (!observer) {
354      HiLog.i(TAG, `registerDataChangeObserver: observer is null.`);
355      return;
356    }
357    if (this.observers.length == 0) {
358      this.getDataAbilityHelper().then((dataAbilityHelper) => {
359        if (dataAbilityHelper) {
360          dataAbilityHelper.on("dataChange", Data.CONTENT_URI, this.callback);
361        }
362      }).catch(error => {
363        HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
364      });
365    }
366    const isExist = this.observers.includes(observer);
367    if (isExist) {
368      return HiLog.i(TAG, 'registerDataChangeObserver: Observer has been attached already.');
369    }
370    HiLog.i(TAG, 'registerDataChangeObserver: Attached an observer.');
371    this.observers.push(observer);
372  }
373
374  unRegisterDataChangeObserver(observer:()=>void) {
375    const observerIndex = this.observers.indexOf(observer);
376    if (observerIndex === -1) {
377      HiLog.i(TAG, 'unRegisterDataChangeObserver: Nonexistent observer.');
378      return
379    }
380    this.observers.splice(observerIndex, 1);
381    HiLog.i(TAG, 'unRegisterDataChangeObserver: Detached an observer.');
382    if (this.observers.length == 0) {
383      this.getDataAbilityHelper().then((dataAbilityHelper) => {
384        if (dataAbilityHelper) {
385          dataAbilityHelper.off("dataChange", Data.CONTENT_URI, this.callback);
386        }
387      }).catch(error => {
388        HiLog.w(TAG, 'error:%s' + JSON.stringify(error.message));
389      });
390    }
391  }
392
393  queryContactDataByNumber(numberList, callback) {
394    this.getDataAbilityHelper().then((dataAbilityHelper) => {
395      if (!dataAbilityHelper) {
396        HiLog.e(TAG, "queryContactDataByNumber, dataAbilityHelper is null");
397        callback([]);
398        return;
399      }
400      let resultColumns = [
401        "detail_info",
402        "display_name",
403      ];
404      let condition = new dataSharePredicates.DataSharePredicates();
405      condition.in("detail_info", numberList);
406      condition.and();
407      condition.equalTo("type_id", "5");
408      condition.and();
409      condition.equalTo("is_deleted", "0");
410      dataAbilityHelper.query(Data.CONTENT_URI, condition, resultColumns).then(resultSet => {
411        callback(this.dealContactResultSet(resultSet));
412      }).catch(error => {
413        HiLog.e(TAG, "queryContactDataByNumber query, error: " + JSON.stringify(error.message));
414      })
415    }).catch(error => {
416      HiLog.e(TAG, "queryContactDataByNumber, error: " + JSON.stringify(error.message));
417      callback([]);
418    });
419  }
420
421  dealContactResultSet(resultSet) {
422    let contacts = [];
423    while (resultSet?.goToNextRow()) {
424      let contact: {[key: string]: any} = {};
425      contact.detailInfo = resultSet.getString(0);
426      contact.displayName = resultSet.getString(1);
427      contacts.push(contact);
428    }
429    return contacts;
430  }
431}