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

Commit 82cd5dde authored by linyuh's avatar linyuh Committed by android-build-merger
Browse files

Merge "Integrate CallDetailsActivity with the new call log UI." am: e85ca463

am: 8309ae0d

Change-Id: Ia28515cba0c304f612b53c3bbb21194c8e045a20
parents e48933c3 8309ae0d
Loading
Loading
Loading
Loading
+98 −9
Original line number Diff line number Diff line
@@ -19,9 +19,12 @@ package com.android.dialer.calldetails;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.CallLog;
@@ -35,11 +38,13 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.android.dialer.CoalescedIds;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.assisteddialing.ui.AssistedDialingSettingActivity;
import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.AsyncTaskExecutors;
@@ -62,6 +67,7 @@ import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.postcall.PostCall;
import com.android.dialer.precall.PreCall;
import com.android.dialer.protos.ProtoParsers;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import java.lang.ref.WeakReference;
@@ -71,10 +77,12 @@ import java.util.Map;

/** Displays the details of a specific call log entry. */
public class CallDetailsActivity extends AppCompatActivity {
  private static final int CALL_DETAILS_LOADER_ID = 0;

  public static final String EXTRA_PHONE_NUMBER = "phone_number";
  public static final String EXTRA_HAS_ENRICHED_CALL_DATA = "has_enriched_call_data";
  public static final String EXTRA_CALL_DETAILS_ENTRIES = "call_details_entries";
  public static final String EXTRA_COALESCED_CALL_LOG_IDS = "coalesced_call_log_ids";
  public static final String EXTRA_CONTACT = "contact";
  public static final String EXTRA_CAN_REPORT_CALLER_ID = "can_report_caller_id";
  private static final String EXTRA_CAN_SUPPORT_ASSISTED_DIALING = "can_support_assisted_dialing";
@@ -93,23 +101,47 @@ public class CallDetailsActivity extends AppCompatActivity {
  private DialerContact contact;
  private CallDetailsAdapter adapter;

  // This will be present only when the activity is launched from the new call log UI, i.e., a list
  // of coalesced annotated call log IDs is included in the intent.
  private Optional<CoalescedIds> coalescedCallLogIds = Optional.absent();

  public static boolean isLaunchIntent(Intent intent) {
    return intent.getComponent() != null
        && CallDetailsActivity.class.getName().equals(intent.getComponent().getClassName());
  }

  /**
   * Returns an {@link Intent} for launching the {@link CallDetailsActivity} from the old call log
   * UI.
   */
  public static Intent newInstance(
      Context context,
      @NonNull CallDetailsEntries details,
      @NonNull DialerContact contact,
      CallDetailsEntries details,
      DialerContact contact,
      boolean canReportCallerId,
      boolean canSupportAssistedDialing) {
    Assert.isNotNull(details);
    Assert.isNotNull(contact);
    Intent intent = new Intent(context, CallDetailsActivity.class);
    ProtoParsers.put(intent, EXTRA_CONTACT, Assert.isNotNull(contact));
    ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, Assert.isNotNull(details));
    intent.putExtra(EXTRA_CAN_REPORT_CALLER_ID, canReportCallerId);
    intent.putExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, canSupportAssistedDialing);
    return intent;
  }

  /**
   * Returns an {@link Intent} for launching the {@link CallDetailsActivity} from the new call log
   * UI.
   */
  public static Intent newInstance(
      Context context,
      CoalescedIds coalescedAnnotatedCallLogIds,
      DialerContact contact,
      boolean canReportCallerId,
      boolean canSupportAssistedDialing) {
    Intent intent = new Intent(context, CallDetailsActivity.class);
    ProtoParsers.put(intent, EXTRA_CONTACT, contact);
    ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, details);
    ProtoParsers.put(intent, EXTRA_CONTACT, Assert.isNotNull(contact));
    ProtoParsers.put(
        intent, EXTRA_COALESCED_CALL_LOG_IDS, Assert.isNotNull(coalescedAnnotatedCallLogIds));
    intent.putExtra(EXTRA_CAN_REPORT_CALLER_ID, canReportCallerId);
    intent.putExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, canSupportAssistedDialing);
    return intent;
@@ -166,10 +198,30 @@ public class CallDetailsActivity extends AppCompatActivity {
  }

  private void onHandleIntent(Intent intent) {
    boolean hasCallDetailsEntries = intent.hasExtra(EXTRA_CALL_DETAILS_ENTRIES);
    boolean hasCoalescedCallLogIds = intent.hasExtra(EXTRA_COALESCED_CALL_LOG_IDS);
    Assert.checkArgument(
        (hasCallDetailsEntries && !hasCoalescedCallLogIds)
            || (!hasCallDetailsEntries && hasCoalescedCallLogIds),
        "One and only one of EXTRA_CALL_DETAILS_ENTRIES and EXTRA_COALESCED_CALL_LOG_IDS "
            + "can be included in the intent.");

    contact = ProtoParsers.getTrusted(intent, EXTRA_CONTACT, DialerContact.getDefaultInstance());
    if (hasCallDetailsEntries) {
      entries =
          ProtoParsers.getTrusted(
              intent, EXTRA_CALL_DETAILS_ENTRIES, CallDetailsEntries.getDefaultInstance());
    } else {
      entries = CallDetailsEntries.getDefaultInstance();
      coalescedCallLogIds =
          Optional.of(
              ProtoParsers.getTrusted(
                  intent, EXTRA_COALESCED_CALL_LOG_IDS, CoalescedIds.getDefaultInstance()));
      getLoaderManager()
          .initLoader(
              CALL_DETAILS_LOADER_ID, /* args = */ null, new CallDetailsLoaderCallbacks(this));
    }

    adapter =
        new CallDetailsAdapter(
            this /* context */,
@@ -191,6 +243,43 @@ public class CallDetailsActivity extends AppCompatActivity {
    super.onBackPressed();
  }

  /**
   * {@link LoaderCallbacks} for {@link CallDetailsCursorLoader}, which loads call detail entries
   * from {@link AnnotatedCallLog}.
   */
  private static final class CallDetailsLoaderCallbacks implements LoaderCallbacks<Cursor> {
    private final CallDetailsActivity activity;

    CallDetailsLoaderCallbacks(CallDetailsActivity callDetailsActivity) {
      this.activity = callDetailsActivity;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
      Assert.checkState(activity.coalescedCallLogIds.isPresent());

      return new CallDetailsCursorLoader(activity, activity.coalescedCallLogIds.get());
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
      updateCallDetailsEntries(CallDetailsCursorLoader.toCallDetailsEntries(data));
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
      updateCallDetailsEntries(CallDetailsEntries.getDefaultInstance());
    }

    private void updateCallDetailsEntries(CallDetailsEntries newEntries) {
      activity.entries = newEntries;
      activity.adapter.updateCallDetailsEntries(newEntries.getEntriesList());
      EnrichedCallComponent.get(activity)
          .getEnrichedCallManager()
          .requestAllHistoricalData(activity.contact.getNumber(), newEntries);
    }
  }

  /** Delete specified calls from the call log. */
  private static class DeleteCallsTask extends AsyncTask<Void, Void, Void> {
    // Use a weak reference to hold the Activity so that there is no memory leak.
+3 −1
Original line number Diff line number Diff line
@@ -115,7 +115,9 @@ final class CallDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol

  @Override
  public int getItemCount() {
    return callDetailsEntries.size() + 2; // Header + footer
    return callDetailsEntries.isEmpty()
        ? 0
        : callDetailsEntries.size() + 2; // plus header and footer
  }

  void updateCallDetailsEntries(List<CallDetailsEntry> entries) {
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.calldetails;

import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import com.android.dialer.CoalescedIds;
import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
import com.android.dialer.common.Assert;
import com.android.dialer.duo.DuoConstants;

/**
 * A {@link CursorLoader} that loads call detail entries from {@link AnnotatedCallLog} for {@link
 * CallDetailsActivity}.
 */
public final class CallDetailsCursorLoader extends CursorLoader {

  // Columns in AnnotatedCallLog that are needed to build a CallDetailsEntry proto.
  // Be sure to update (1) constants that store indexes of the elements and (2) method
  // toCallDetailsEntry(Cursor) when updating this array.
  public static final String[] COLUMNS_FOR_CALL_DETAILS =
      new String[] {
        AnnotatedCallLog._ID,
        AnnotatedCallLog.CALL_TYPE,
        AnnotatedCallLog.FEATURES,
        AnnotatedCallLog.TIMESTAMP,
        AnnotatedCallLog.DURATION,
        AnnotatedCallLog.DATA_USAGE,
        AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME
      };

  // Indexes for COLUMNS_FOR_CALL_DETAILS
  private static final int ID = 0;
  private static final int CALL_TYPE = 1;
  private static final int FEATURES = 2;
  private static final int TIMESTAMP = 3;
  private static final int DURATION = 4;
  private static final int DATA_USAGE = 5;
  private static final int PHONE_ACCOUNT_COMPONENT_NAME = 6;

  CallDetailsCursorLoader(Context context, CoalescedIds coalescedIds) {
    super(
        context,
        AnnotatedCallLog.CONTENT_URI,
        COLUMNS_FOR_CALL_DETAILS,
        annotatedCallLogIdsSelection(coalescedIds),
        annotatedCallLogIdsSelectionArgs(coalescedIds),
        AnnotatedCallLog.TIMESTAMP + " DESC");
  }

  /**
   * Build a string of the form "COLUMN_NAME IN (?, ?, ..., ?)", where COLUMN_NAME is the name of
   * the ID column in {@link AnnotatedCallLog}.
   *
   * <p>This string will be used as the {@code selection} parameter to initialize the loader.
   */
  private static String annotatedCallLogIdsSelection(CoalescedIds coalescedIds) {
    // First, build a string of question marks ('?') separated by commas (',').
    StringBuilder questionMarks = new StringBuilder();
    for (int i = 0; i < coalescedIds.getCoalescedIdCount(); i++) {
      if (i != 0) {
        questionMarks.append(", ");
      }
      questionMarks.append("?");
    }

    return AnnotatedCallLog._ID + " IN (" + questionMarks + ")";
  }

  /**
   * Returns a string that will be used as the {@code selectionArgs} parameter to initialize the
   * loader.
   */
  private static String[] annotatedCallLogIdsSelectionArgs(CoalescedIds coalescedIds) {
    String[] args = new String[coalescedIds.getCoalescedIdCount()];

    for (int i = 0; i < coalescedIds.getCoalescedIdCount(); i++) {
      args[i] = String.valueOf(coalescedIds.getCoalescedId(i));
    }

    return args;
  }

  /**
   * Creates a new {@link CallDetailsEntries} from the entire data set loaded by this loader.
   *
   * @param cursor A cursor pointing to the data set loaded by this loader. The caller must ensure
   *     the cursor is not null and the data set it points to is not empty.
   * @return A {@link CallDetailsEntries} proto.
   */
  static CallDetailsEntries toCallDetailsEntries(Cursor cursor) {
    Assert.isNotNull(cursor);
    Assert.checkArgument(cursor.moveToFirst());

    CallDetailsEntries.Builder entries = CallDetailsEntries.newBuilder();

    do {
      entries.addEntries(toCallDetailsEntry(cursor));
    } while (cursor.moveToNext());

    return entries.build();
  }

  /** Creates a new {@link CallDetailsEntry} from the provided cursor using its current position. */
  private static CallDetailsEntry toCallDetailsEntry(Cursor cursor) {
    CallDetailsEntry.Builder entry = CallDetailsEntry.newBuilder();
    entry
        .setCallId(cursor.getLong(ID))
        .setCallType(cursor.getInt(CALL_TYPE))
        .setFeatures(cursor.getInt(FEATURES))
        .setDate(cursor.getLong(TIMESTAMP))
        .setDuration(cursor.getLong(DURATION))
        .setDataUsage(cursor.getLong(DATA_USAGE));

    String phoneAccountComponentName = cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME);
    entry.setIsDuoCall(
        DuoConstants.PHONE_ACCOUNT_COMPONENT_NAME
            .flattenToString()
            .equals(phoneAccountComponentName));

    return entry.build();
  }
}
+6 −8
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.provider.ContactsContract;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
import com.android.dialer.calldetails.CallDetailsActivity;
import com.android.dialer.calldetails.CallDetailsEntries;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.calllog.model.CoalescedRow;
import com.android.dialer.calllogutils.PhoneAccountUtils;
@@ -65,7 +64,7 @@ final class Modules {

    // TODO(zachh): Revisit if DialerContact is the best thing to pass to CallDetails; could
    // it use a ContactPrimaryActionInfo instead?
    addModuleForAccessingCallDetails(context, createDialerContactFromRow(row), modules);
    addModuleForAccessingCallDetails(context, row, modules);

    return modules;
  }
@@ -182,10 +181,9 @@ final class Modules {
  }

  private static void addModuleForAccessingCallDetails(
      Context context, DialerContact dialerContact, List<ContactActionModule> modules) {
    // TODO(zachh): Load CallDetailsEntries and canReportInaccurateNumber in
    // CallDetailsActivity (see also isPeopleApiSource(sourceType)).
    CallDetailsEntries callDetailsEntries = CallDetailsEntries.getDefaultInstance();
      Context context, CoalescedRow row, List<ContactActionModule> modules) {
    // TODO(zachh): Load canReportInaccurateNumber in CallDetailsActivity
    // (see also isPeopleApiSource(sourceType)).
    boolean canReportInaccurateNumber = false;
    boolean canSupportAssistedDialing = false; // TODO(zachh): Properly set value.

@@ -194,8 +192,8 @@ final class Modules {
            context,
            CallDetailsActivity.newInstance(
                context,
                callDetailsEntries,
                dialerContact,
                row.coalescedIds(),
                createDialerContactFromRow(row),
                canReportInaccurateNumber,
                canSupportAssistedDialing),
            R.string.call_details_menu_label,