Loading java/com/android/dialer/contacts/ContactsComponent.java +5 −0 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.dialer.contacts; import android.content.Context; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequester; import com.android.dialer.inject.HasRootComponent; import com.android.dialer.inject.IncludeInDialerRoot; import dagger.Subcomponent; /** Component for contacts related utilities */ Loading @@ -27,12 +29,15 @@ public abstract class ContactsComponent { public abstract ContactDisplayPreferences contactDisplayPreferences(); public abstract HighResolutionPhotoRequester highResolutionPhotoLoader(); public static ContactsComponent get(Context context) { return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) .contactsComponent(); } /** Used to refer to the root application component. */ @IncludeInDialerRoot public interface HasComponent { ContactsComponent contactsComponent(); } Loading java/com/android/dialer/contacts/ContactsModule.java +8 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.dialer.contacts; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferencesImpl; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequester; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequesterImpl; import com.android.dialer.inject.DialerVariant; import com.android.dialer.inject.InstallIn; import dagger.Binds; Loading @@ -28,5 +30,10 @@ import dagger.Module; @Module public abstract class ContactsModule { @Binds public abstract ContactDisplayPreferences to(ContactDisplayPreferencesImpl impl); public abstract ContactDisplayPreferences toContactDisplayPreferencesImpl( ContactDisplayPreferencesImpl impl); @Binds public abstract HighResolutionPhotoRequester toHighResolutionPhotoRequesterImpl( HighResolutionPhotoRequesterImpl impl); } java/com/android/dialer/contacts/hiresphoto/HighResolutionPhotoRequester.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * 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.net.Uri; import com.google.common.util.concurrent.ListenableFuture; /** * Requests the contacts sync adapter to load a high resolution photo for the contact, typically * when we will try to show the contact in a larger view (favorites, incall UI, etc.). If a high * resolution photo is synced, the uri will be notified. */ public interface HighResolutionPhotoRequester { ListenableFuture<Void> request(Uri contactUri); } java/com/android/dialer/contacts/hiresphoto/HighResolutionPhotoRequesterImpl.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * 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<Void> 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<Long> getGoogleRawContactIds(long contactId) throws RequestFailedException { List<Long> 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; } } java/com/android/dialer/main/impl/toolbar/res/drawable/search_bar_background_rounded_corners.xml +1 −1 Original line number Diff line number Diff line Loading @@ -17,5 +17,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="?android:attr/colorBackgroundFloating"/> <corners android:radius="2dp"/> <corners android:radius="8dp"/> </shape> Loading
java/com/android/dialer/contacts/ContactsComponent.java +5 −0 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.dialer.contacts; import android.content.Context; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequester; import com.android.dialer.inject.HasRootComponent; import com.android.dialer.inject.IncludeInDialerRoot; import dagger.Subcomponent; /** Component for contacts related utilities */ Loading @@ -27,12 +29,15 @@ public abstract class ContactsComponent { public abstract ContactDisplayPreferences contactDisplayPreferences(); public abstract HighResolutionPhotoRequester highResolutionPhotoLoader(); public static ContactsComponent get(Context context) { return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) .contactsComponent(); } /** Used to refer to the root application component. */ @IncludeInDialerRoot public interface HasComponent { ContactsComponent contactsComponent(); } Loading
java/com/android/dialer/contacts/ContactsModule.java +8 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.dialer.contacts; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences; import com.android.dialer.contacts.displaypreference.ContactDisplayPreferencesImpl; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequester; import com.android.dialer.contacts.hiresphoto.HighResolutionPhotoRequesterImpl; import com.android.dialer.inject.DialerVariant; import com.android.dialer.inject.InstallIn; import dagger.Binds; Loading @@ -28,5 +30,10 @@ import dagger.Module; @Module public abstract class ContactsModule { @Binds public abstract ContactDisplayPreferences to(ContactDisplayPreferencesImpl impl); public abstract ContactDisplayPreferences toContactDisplayPreferencesImpl( ContactDisplayPreferencesImpl impl); @Binds public abstract HighResolutionPhotoRequester toHighResolutionPhotoRequesterImpl( HighResolutionPhotoRequesterImpl impl); }
java/com/android/dialer/contacts/hiresphoto/HighResolutionPhotoRequester.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * 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.net.Uri; import com.google.common.util.concurrent.ListenableFuture; /** * Requests the contacts sync adapter to load a high resolution photo for the contact, typically * when we will try to show the contact in a larger view (favorites, incall UI, etc.). If a high * resolution photo is synced, the uri will be notified. */ public interface HighResolutionPhotoRequester { ListenableFuture<Void> request(Uri contactUri); }
java/com/android/dialer/contacts/hiresphoto/HighResolutionPhotoRequesterImpl.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * 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<Void> 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<Long> getGoogleRawContactIds(long contactId) throws RequestFailedException { List<Long> 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; } }
java/com/android/dialer/main/impl/toolbar/res/drawable/search_bar_background_rounded_corners.xml +1 −1 Original line number Diff line number Diff line Loading @@ -17,5 +17,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="?android:attr/colorBackgroundFloating"/> <corners android:radius="2dp"/> <corners android:radius="8dp"/> </shape>