/* * Copyright (C) 2018 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.dialer.contacts.hiresphoto; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.RawContacts; import android.support.annotation.VisibleForTesting; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.common.database.Selection; import com.android.dialer.inject.ApplicationContext; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** Use the contacts sync adapter to load high resolution photos for a Google account. */ public class HighResolutionPhotoRequesterImpl implements HighResolutionPhotoRequester { private static class RequestFailedException extends Exception { RequestFailedException(String message) { super(message); } RequestFailedException(String message, Throwable cause) { super(message, cause); } } @VisibleForTesting static final ComponentName SYNC_HIGH_RESOLUTION_PHOTO_SERVICE = new ComponentName( "com.google.android.syncadapters.contacts", "com.google.android.syncadapters.contacts.SyncHighResPhotoIntentService"); private final Context appContext; private final ListeningExecutorService backgroundExecutor; @Inject HighResolutionPhotoRequesterImpl( @ApplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService backgroundExecutor) { this.appContext = appContext; this.backgroundExecutor = backgroundExecutor; } @Override public ListenableFuture request(Uri contactUri) { return backgroundExecutor.submit( () -> { try { requestInternal(contactUri); } catch (RequestFailedException e) { LogUtil.e("HighResolutionPhotoRequesterImpl.request", "request failed", e); } return null; }); } private void requestInternal(Uri contactUri) throws RequestFailedException { for (Long rawContactId : getGoogleRawContactIds(getContactId(contactUri))) { Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setComponent(SYNC_HIGH_RESOLUTION_PHOTO_SERVICE); intent.setDataAndType(rawContactUri, RawContacts.CONTENT_ITEM_TYPE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); try { LogUtil.i( "HighResolutionPhotoRequesterImpl.requestInternal", "requesting photo for " + rawContactUri); appContext.startService(intent); } catch (IllegalStateException | SecurityException e) { throw new RequestFailedException("unable to start sync adapter", e); } } } private long getContactId(Uri contactUri) throws RequestFailedException { try (Cursor cursor = appContext .getContentResolver() .query(contactUri, new String[] {Contacts._ID}, null, null, null)) { if (cursor == null || !cursor.moveToFirst()) { throw new RequestFailedException("cannot get contact ID"); } return cursor.getLong(0); } } private List getGoogleRawContactIds(long contactId) throws RequestFailedException { List result = new ArrayList<>(); Selection selection = Selection.column(RawContacts.CONTACT_ID) .is("=", contactId) .buildUpon() .and(Selection.column(RawContacts.ACCOUNT_TYPE).is("=", "com.google")) .build(); try (Cursor cursor = appContext .getContentResolver() .query( RawContacts.CONTENT_URI, new String[] {RawContacts._ID, RawContacts.ACCOUNT_TYPE}, selection.getSelection(), selection.getSelectionArgs(), null)) { if (cursor == null) { throw new RequestFailedException("null cursor from raw contact IDs"); } for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { result.add(cursor.getLong(0)); } } return result; } }