/* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: * * The original file was part of Open Source IMSDROID * Copyright (C) 2010-2011, Mamadou Diop. * Copyright (C) 2011, Doubango Telecom. * * * Contact: Mamadou Diop * * This file is part of Open Source Doubango Framework. * * This is free software: you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.doubango.ngn.services.impl; import android.app.Activity; import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; import android.os.Looper; import android.provider.ContactsContract; import android.util.Log; import org.doubango.ngn.NgnApplication; import org.doubango.ngn.NgnEngine; import org.doubango.ngn.model.NgnContact; import org.doubango.ngn.model.NgnPhoneNumber; import org.doubango.ngn.services.INgnContactService; import org.doubango.ngn.utils.NgnCallbackFunc; import org.doubango.ngn.utils.NgnListUtils; import org.doubango.ngn.utils.NgnObservableList; import org.doubango.ngn.utils.NgnPredicate; import org.doubango.ngn.utils.NgnStringUtils; import org.doubango.tinyWRAP.SipUri; import org.doubango.utils.Utils; import java.util.ArrayList; import java.util.List; /**@page NgnContactService_page Contact Service * The Contact service is used to retrieve contacts from the native address book. * */ /** * Service used to retrieve contacts from the native address book. */ public class NgnContactService extends NgnBaseService implements INgnContactService{ private final static String TAG = Utils.getTAG(NgnContactService.class.getCanonicalName()); public final static String PARAM_ARG_CONTACT = "NgnContact"; protected NgnObservableList mContacts; protected boolean mLoading; protected boolean mReady; protected Looper mLocalContactObserverLooper; protected ContentObserver mLocalContactObserver; private NgnCallbackFunc mOnBeginLoadCallback; private NgnCallbackFunc mOnNewPhoneNumberCallback; private NgnCallbackFunc mOnEndLoadCallback; public NgnContactService(){ super(); } @Override public boolean start() { Log.d(TAG, "Starting..."); if(mContacts == null){ mContacts = getObservableContacts(); } /* if(mLocalContactObserver == null && mLocalContactObserverLooper == null){ new Thread(new Runnable() { // avoid locking calling thread @Override public void run() { Log.d(TAG, "Observer loop enter()"); Looper.prepare(); mLocalContactObserverLooper = Looper.myLooper(); final Handler handler = new Handler(); handler.post(new Runnable() { // Observer thread. Will allow us to get notifications even if the application is on background @Override public void run() { mLocalContactObserver = new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); Log.d(TAG, "Native address book changed."); load(); } }; NgnApplication.getContext().getContentResolver().registerContentObserver(CommonDataKinds.Phone.CONTENT_URI, true, mLocalContactObserver); } }); Looper.loop();// loop() until quit() is called Log.d(TAG, "Observer loop exit()"); } }).start(); }; */ return true; } @Override public boolean stop() { Log.d(TAG, "Stopping..."); try{ if(mLocalContactObserver != null){ NgnApplication.getContext().getContentResolver().unregisterContentObserver(mLocalContactObserver); mLocalContactObserver = null; } if(mLocalContactObserverLooper != null){ mLocalContactObserverLooper.quit(); mLocalContactObserverLooper = null; } } catch(Exception e){ Log.e(TAG, e.toString()); } return true; } @Override public void setOnBeginLoadCallback(NgnCallbackFunc callback){ mOnBeginLoadCallback = callback; } @Override public void setOnNewPhoneNumberCallback(NgnCallbackFunc callback){ mOnNewPhoneNumberCallback = callback; } @Override public void setOnEndLoadCallback(NgnCallbackFunc callback){ mOnEndLoadCallback = callback; } @Override public boolean load(){ return load2(); } public boolean load2(){ mLoading = true; boolean bOK = false; Cursor managedCursor = null; final Activity activity = NgnEngine.getInstance().getMainActivity(); final List contactsCopy = new ArrayList(); if(mOnBeginLoadCallback != null){ mOnBeginLoadCallback.callback(this); } try{ String phoneNumber, displayName, label; NgnContact contact = null; int id, type, photoId; final Resources res = NgnApplication.getContext().getResources(); if(NgnApplication.getSDKVersion() >=5 && activity != null){ /*synchronized(mContacts)*/{ final String[] projectionContacts = new String[] { android.provider.BaseColumns._ID, android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER, android.provider.ContactsContract.CommonDataKinds.Phone.TYPE, android.provider.ContactsContract.CommonDataKinds.Phone.LABEL, android.provider.ContactsContract.Contacts.DISPLAY_NAME, android.provider.ContactsContract.Contacts.PHOTO_ID, android.provider.ContactsContract.CommonDataKinds.Phone.CONTACT_ID, }; managedCursor = activity.managedQuery(android.provider.ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectionContacts, // Which columns to return null, // Which rows to return (all rows) null, // Selection arguments (none) // Put the results in ascending order by name "UPPER(" + android.provider.ContactsContract.Contacts.DISPLAY_NAME + ") ASC" ); int indexPhoneContactId = managedCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID); int indexPhoneType = managedCursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE); int indexPhoneLabel = managedCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL); int indexPhoneNumber = managedCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); int indexPhonePhotoId = managedCursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_ID); int indexContactDisplayName = managedCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); while(managedCursor.moveToNext()){ id = managedCursor.getInt(indexPhoneContactId); type = managedCursor.getInt(indexPhoneType); label = managedCursor.getString(indexPhoneLabel); phoneNumber = managedCursor.getString(indexPhoneNumber); photoId = managedCursor.getInt(indexPhonePhotoId); if(phoneNumber != null){ phoneNumber = phoneNumber.replace("-", ""); if(NgnStringUtils.isNullOrEmpty(label)){ final int resId = android.provider.ContactsContract.CommonDataKinds.Phone.getTypeLabelResource(type); label = res.getString(resId); } if(contact == null || contact.getId() != id){ displayName = managedCursor.getString(indexContactDisplayName); contact = newContact(id, displayName); if(photoId != 0){ contact.setPhotoId(photoId); } contactsCopy.add(contact); } contact.addPhoneNumber(NgnPhoneNumber.fromAndroid2LocalType(type), phoneNumber, label); if(mOnNewPhoneNumberCallback != null){ mOnNewPhoneNumberCallback.callback(phoneNumber); } } } mLoading = false; mReady = true; bOK = true; } } } catch(Exception e){ e.printStackTrace(); mLoading = false; mReady = false; } finally{ if(managedCursor != null){ activity.stopManagingCursor(managedCursor); managedCursor.close(); } } if(bOK){ synchronized(mContacts){ mContacts.clear(); mContacts.add(contactsCopy); } } if(mOnEndLoadCallback != null){ mOnEndLoadCallback.callback(this); } return bOK; } @Override public boolean isLoading() { return mLoading; } @Override public boolean isReady(){ return mReady; } @Override public NgnObservableList getObservableContacts() { if(mContacts == null){ mContacts = new NgnObservableList(true); } return mContacts; } @Override public NgnContact newContact(int id, String displayName){ return new NgnContact(id, displayName); } @Override public NgnContact getContactByUri(String uri) { final SipUri sipUri = new SipUri(uri); NgnContact contact = null; if(sipUri.isValid()){ contact = getContactByPhoneNumber(sipUri.getUserName()); } sipUri.delete(); return contact; } @Override public NgnContact getContactByPhoneNumber(String anyPhoneNumber) { return NgnListUtils.getFirstOrDefault(mContacts.getList(), new NgnContact.ContactFilterByAnyPhoneNumber(anyPhoneNumber)); } static class ContactFilterById implements NgnPredicate{ private final int mId; ContactFilterById(int id){ mId = id; } @Override public boolean apply(NgnContact contact) { return (contact != null && contact.getId() == mId); } } @Override public boolean clearService(){ return true; } }