Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 797aec0f authored by twyen's avatar twyen Committed by android-build-merger
Browse files

Merge changes Ie3a173b7,I2afbd7d9,Id6121d88 am: 2e3e5ff3

am: 33add58a

Change-Id: If006938e2d8627404ab086a4d963b5508f5bde43
parents 517572cd 33add58a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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 */
@@ -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();
  }
+8 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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);
}
+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);
}
+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;
  }
}
+1 −1
Original line number Diff line number Diff line
@@ -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