/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.inputmethod.latin; import android.Manifest; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.os.SystemClock; import android.provider.ContactsContract.Contacts; import android.util.Log; import com.android.inputmethod.latin.ContactsManager.ContactsChangedListener; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.permissions.PermissionsUtil; import com.android.inputmethod.latin.utils.ExecutorUtils; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** * A content observer that listens to updates to content provider {@link Contacts#CONTENT_URI}. */ public class ContactsContentObserver implements Runnable { private static final String TAG = "ContactsContentObserver"; private final Context mContext; private final ContactsManager mManager; private final AtomicBoolean mRunning = new AtomicBoolean(false); private ContentObserver mContentObserver; private ContactsChangedListener mContactsChangedListener; public ContactsContentObserver(final ContactsManager manager, final Context context) { mManager = manager; mContext = context; } public void registerObserver(final ContactsChangedListener listener) { if (!PermissionsUtil.checkAllPermissionsGranted( mContext, Manifest.permission.READ_CONTACTS)) { Log.i(TAG, "No permission to read contacts. Not registering the observer."); // do nothing if we do not have the permission to read contacts. return; } if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "registerObserver()"); } mContactsChangedListener = listener; mContentObserver = new ContentObserver(null /* handler */) { @Override public void onChange(boolean self) { ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD) .execute(ContactsContentObserver.this); } }; final ContentResolver contentResolver = mContext.getContentResolver(); contentResolver.registerContentObserver(Contacts.CONTENT_URI, true, mContentObserver); } @Override public void run() { if (!PermissionsUtil.checkAllPermissionsGranted( mContext, Manifest.permission.READ_CONTACTS)) { Log.i(TAG, "No permission to read contacts. Not updating the contacts."); unregister(); return; } if (!mRunning.compareAndSet(false /* expect */, true /* update */)) { if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "run() : Already running. Don't waste time checking again."); } return; } if (haveContentsChanged()) { if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "run() : Contacts have changed. Notifying listeners."); } mContactsChangedListener.onContactsChange(); } mRunning.set(false); } boolean haveContentsChanged() { if (!PermissionsUtil.checkAllPermissionsGranted( mContext, Manifest.permission.READ_CONTACTS)) { Log.i(TAG, "No permission to read contacts. Marking contacts as not changed."); return false; } final long startTime = SystemClock.uptimeMillis(); final int contactCount = mManager.getContactCount(); if (contactCount > ContactsDictionaryConstants.MAX_CONTACTS_PROVIDER_QUERY_LIMIT) { // If there are too many contacts then return false. In this rare case it is impossible // to include all of them anyways and the cost of rebuilding the dictionary is too high. // TODO: Sort and check only the most recent contacts? return false; } if (contactCount != mManager.getContactCountAtLastRebuild()) { if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "haveContentsChanged() : Count changed from " + mManager.getContactCountAtLastRebuild() + " to " + contactCount); } return true; } final ArrayList names = mManager.getValidNames(Contacts.CONTENT_URI); if (names.hashCode() != mManager.getHashCodeAtLastRebuild()) { return true; } if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "haveContentsChanged() : No change detected in " + (SystemClock.uptimeMillis() - startTime) + " ms)"); } return false; } public void unregister() { mContext.getContentResolver().unregisterContentObserver(mContentObserver); } }