14084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani/* 24084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * Copyright (C) 2014 The Android Open Source Project 34084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * 44084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * Licensed under the Apache License, Version 2.0 (the "License"); 54084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * you may not use this file except in compliance with the License. 64084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * You may obtain a copy of the License at 74084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * 84084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * http://www.apache.org/licenses/LICENSE-2.0 94084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * 104084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * Unless required by applicable law or agreed to in writing, software 114084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * distributed under the License is distributed on an "AS IS" BASIS, 124084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * See the License for the specific language governing permissions and 144084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani * limitations under the License. 154084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani */ 164084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 174084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matanipackage com.android.inputmethod.latin; 184084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 19604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheikimport android.Manifest; 204084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.content.ContentResolver; 214084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.content.Context; 224084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.database.ContentObserver; 234084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.os.SystemClock; 244084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.provider.ContactsContract.Contacts; 254084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport android.util.Log; 264084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 274084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport com.android.inputmethod.latin.ContactsManager.ContactsChangedListener; 28541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovicimport com.android.inputmethod.latin.define.DebugFlags; 29604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheikimport com.android.inputmethod.latin.permissions.PermissionsUtil; 304084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport com.android.inputmethod.latin.utils.ExecutorUtils; 314084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 324084fa5caeee09ef7993957c5e922dab14c57f3fJatin Mataniimport java.util.ArrayList; 33705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovicimport java.util.concurrent.atomic.AtomicBoolean; 344084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 354084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani/** 360b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic * A content observer that listens to updates to content provider {@link Contacts#CONTENT_URI}. 374084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani */ 380b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovicpublic class ContactsContentObserver implements Runnable { 39541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic private static final String TAG = "ContactsContentObserver"; 404084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 414084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani private final Context mContext; 424084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani private final ContactsManager mManager; 43604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik private final AtomicBoolean mRunning = new AtomicBoolean(false); 444084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 45705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic private ContentObserver mContentObserver; 46705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic private ContactsChangedListener mContactsChangedListener; 47705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic 484084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani public ContactsContentObserver(final ContactsManager manager, final Context context) { 494084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani mManager = manager; 504084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani mContext = context; 514084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 524084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 534084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani public void registerObserver(final ContactsChangedListener listener) { 54604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik if (!PermissionsUtil.checkAllPermissionsGranted( 55604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik mContext, Manifest.permission.READ_CONTACTS)) { 56604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik Log.i(TAG, "No permission to read contacts. Not registering the observer."); 57604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik // do nothing if we do not have the permission to read contacts. 58604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik return; 59604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik } 60604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik 61541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic if (DebugFlags.DEBUG_ENABLED) { 62541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic Log.d(TAG, "registerObserver()"); 634084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 640b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic mContactsChangedListener = listener; 650b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic mContentObserver = new ContentObserver(null /* handler */) { 664084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani @Override 674084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani public void onChange(boolean self) { 68eaa710d4aaac75ff2b7e29608d004fe7662b392eDan Zivkovic ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD) 69eaa710d4aaac75ff2b7e29608d004fe7662b392eDan Zivkovic .execute(ContactsContentObserver.this); 704084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 714084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani }; 724084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani final ContentResolver contentResolver = mContext.getContentResolver(); 730b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic contentResolver.registerContentObserver(Contacts.CONTENT_URI, true, mContentObserver); 744084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 754084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 760b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic @Override 770b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic public void run() { 78604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik if (!PermissionsUtil.checkAllPermissionsGranted( 79604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik mContext, Manifest.permission.READ_CONTACTS)) { 80604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik Log.i(TAG, "No permission to read contacts. Not updating the contacts."); 81604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik unregister(); 82604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik return; 83604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik } 84604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik 85604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik if (!mRunning.compareAndSet(false /* expect */, true /* update */)) { 86541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic if (DebugFlags.DEBUG_ENABLED) { 87705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic Log.d(TAG, "run() : Already running. Don't waste time checking again."); 88705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic } 89705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic return; 90705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic } 910b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic if (haveContentsChanged()) { 92541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic if (DebugFlags.DEBUG_ENABLED) { 93705b118672ce5680257b31c565c520e95fd8e298Dan Zivkovic Log.d(TAG, "run() : Contacts have changed. Notifying listeners."); 940b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic } 950b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic mContactsChangedListener.onContactsChange(); 960b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic } 97604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik mRunning.set(false); 984084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 994084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 10001b023730ee3d86d60016c21915608376c724442Jatin Matani boolean haveContentsChanged() { 101604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik if (!PermissionsUtil.checkAllPermissionsGranted( 102604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik mContext, Manifest.permission.READ_CONTACTS)) { 103604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik Log.i(TAG, "No permission to read contacts. Marking contacts as not changed."); 104604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik return false; 105604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik } 106604158669b407a40cd0f23538fad4dce5d738f24Mohammadinamul Sheik 1074084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani final long startTime = SystemClock.uptimeMillis(); 1084084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani final int contactCount = mManager.getContactCount(); 10944a175732dc4b872515f978b986ef7b357fe2f00Tom Ouyang if (contactCount > ContactsDictionaryConstants.MAX_CONTACTS_PROVIDER_QUERY_LIMIT) { 1104084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani // If there are too many contacts then return false. In this rare case it is impossible 1114084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani // to include all of them anyways and the cost of rebuilding the dictionary is too high. 11244a175732dc4b872515f978b986ef7b357fe2f00Tom Ouyang // TODO: Sort and check only the most recent contacts? 1134084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani return false; 1144084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1154084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani if (contactCount != mManager.getContactCountAtLastRebuild()) { 116541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic if (DebugFlags.DEBUG_ENABLED) { 117541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic Log.d(TAG, "haveContentsChanged() : Count changed from " 118541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic + mManager.getContactCountAtLastRebuild() + " to " + contactCount); 1194084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1204084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani return true; 1214084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1224084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani final ArrayList<String> names = mManager.getValidNames(Contacts.CONTENT_URI); 1234084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani if (names.hashCode() != mManager.getHashCodeAtLastRebuild()) { 1244084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani return true; 1254084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 126541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic if (DebugFlags.DEBUG_ENABLED) { 127541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic Log.d(TAG, "haveContentsChanged() : No change detected in " 128541ef56e057eb7d81eae6e294ce9eb364f825867Dan Zivkovic + (SystemClock.uptimeMillis() - startTime) + " ms)"); 1294084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1304084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani return false; 1314084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1324084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani 1334084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani public void unregister() { 1340b03f13cabec84d2d841fde47ce9fec0d531b6a1Dan Zivkovic mContext.getContentResolver().unregisterContentObserver(mContentObserver); 1354084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani } 1364084fa5caeee09ef7993957c5e922dab14c57f3fJatin Matani} 137