blob: cbfe5a48d05f798c1d6f966b254eac2690405b04 [file] [log] [blame]
/*
* Copyright (C) 2016 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.car.apps.common;
import static android.Manifest.permission.GET_ACCOUNTS;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.RequiresPermission;
import android.content.ContentUris;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import java.util.HashMap;
import java.util.LinkedHashSet;
/**
* @hide
*/
public class AccountImageChangeObserver {
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
private static final Object sObserverInstanceLock = new Object();
private static AccountImageChangeObserver sObserver;
private class ContactChangeContentObserver extends ContentObserver {
private final Account mWatchedAccount;
private final LinkedHashSet<Uri> mUrisToNotify;
private final Object mLock = new Object();
private final Context mContext;
private String mCurrentImageUri;
public ContactChangeContentObserver(Context context, Account watchedAccount) {
super(null);
mWatchedAccount = watchedAccount;
mUrisToNotify = new LinkedHashSet<>();
mContext = context;
mCurrentImageUri = AccountImageHelper.getAccountPictureUri(mContext, mWatchedAccount);
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
public void addUriToNotifyList(Uri uri) {
synchronized (mLock) {
mUrisToNotify.add(uri);
}
}
@Override
public void onChange(boolean selfChange) {
String newUri = AccountImageHelper.getAccountPictureUri(mContext, mWatchedAccount);
if (TextUtils.equals(mCurrentImageUri, newUri)) {
// no change, no need to notify
return;
}
synchronized (mLock) {
for (Uri uri : mUrisToNotify) {
mContext.getContentResolver().notifyChange(uri, null);
}
mCurrentImageUri = newUri;
}
}
}
private final HashMap<String, ContactChangeContentObserver> mObserverMap;
/**
* get the singleton AccountImageChangeObserver for the application
*/
public final static AccountImageChangeObserver getInstance() {
if (sObserver == null) {
synchronized (sObserverInstanceLock) {
if (sObserver == null) {
sObserver = new AccountImageChangeObserver();
}
}
}
return sObserver;
}
public AccountImageChangeObserver() {
mObserverMap = new HashMap<>();
}
@RequiresPermission(GET_ACCOUNTS)
public synchronized void registerChangeUriIfPresent(BitmapWorkerOptions options) {
Uri imageUri = options.getResourceUri();
// Only register URIs that match the Account Image URI schema, and
// have a change notify URI specified.
if (imageUri != null && UriUtils.isAccountImageUri(imageUri)) {
Uri changeNotifUri = UriUtils.getAccountImageChangeNotifyUri(imageUri);
imageUri = imageUri.buildUpon().clearQuery().build();
if (changeNotifUri == null) {
// No change Notiy URI specified
return;
}
String accountName = UriUtils.getAccountName(imageUri);
Context context = options.getContext();
if (accountName != null && context != null) {
Account thisAccount = null;
for (Account account : AccountManager.get(context).
getAccountsByType(GOOGLE_ACCOUNT_TYPE)) {
if (account.name.equals(accountName)) {
thisAccount = account;
break;
}
}
if (thisAccount != null) {
ContactChangeContentObserver observer;
if (mObserverMap.containsKey(thisAccount.name)) {
observer = mObserverMap.get(thisAccount.name);
if (observer != null) {
observer.addUriToNotifyList(changeNotifUri);
}
} else {
long contactId = getContactIdForAccount(context, thisAccount);
if (contactId != -1) {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
contactId);
observer = new ContactChangeContentObserver(context, thisAccount);
mObserverMap.put(thisAccount.name, observer);
observer.addUriToNotifyList(changeNotifUri);
context.getContentResolver().registerContentObserver(contactUri, false,
observer);
}
}
}
}
}
}
private long getContactIdForAccount(Context context, Account account) {
// Look up this account in the contacts database.
String[] projection = new String[] {
ContactsContract.Data._ID,
ContactsContract.Data.CONTACT_ID,
ContactsContract.Data.LOOKUP_KEY
};
String selection =
ContactsContract.CommonDataKinds.Email.ADDRESS + " LIKE ?";
String[] selectionArgs = new String[] { account.name };
Cursor c = null;
long contactId = -1;
String lookupKey = null;
try {
c = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
projection, selection, selectionArgs, null);
if (c.moveToNext()) {
contactId = c.getLong(1);
lookupKey = c.getString(2);
}
} finally {
if (c != null) {
c.close();
}
}
if (contactId != -1 && !TextUtils.isEmpty(lookupKey)) {
return contactId;
}
return -1;
}
}