blob: 94c2f001827bc3991fb5619e843966ddf9bc5a26 [file] [log] [blame]
/*
* Copyright (C) 2009 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;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.v7.app.AppCompatActivity;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import android.widget.Toast;
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.ClipboardUtils;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.compat.CompatUtils;
import com.android.contacts.common.interactions.TouchPointManager;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.UriUtils;
import com.android.dialer.calllog.CallDetailHistoryAdapter;
import com.android.dialer.calllog.CallLogAsyncTaskUtil;
import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
import com.android.dialer.calllog.CallTypeHelper;
import com.android.dialer.calllog.ContactInfoHelper;
import com.android.dialer.calllog.PhoneAccountUtils;
import com.android.dialer.compat.FilteredNumberCompat;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
import com.android.dialer.filterednumber.BlockNumberDialogFragment;
import com.android.dialer.filterednumber.FilteredNumbersUtil;
import com.android.dialer.logging.InteractionEvent;
import com.android.dialer.logging.Logger;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.util.TelecomUtil;
import com.android.incallui.Call.LogState;
/**
* Displays the details of a specific call log entry.
* <p>
* This activity can be either started with the URI of a single call log entry, or with the
* {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
*/
public class CallDetailActivity extends AppCompatActivity
implements MenuItem.OnMenuItemClickListener, View.OnClickListener,
BlockNumberDialogFragment.Callback {
private static final String TAG = CallDetailActivity.class.getSimpleName();
/** A long array extra containing ids of call log entries to display. */
public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
/** If we are started with a voicemail, we'll find the uri to play with this extra. */
public static final String EXTRA_VOICEMAIL_URI = "EXTRA_VOICEMAIL_URI";
/** If the activity was triggered from a notification. */
public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
public static final String VOICEMAIL_FRAGMENT_TAG = "voicemail_fragment";
private CallLogAsyncTaskListener mCallLogAsyncTaskListener = new CallLogAsyncTaskListener() {
@Override
public void onDeleteCall() {
finish();
}
@Override
public void onDeleteVoicemail() {
finish();
}
@Override
public void onGetCallDetails(PhoneCallDetails[] details) {
if (details == null) {
// Somewhere went wrong: we're going to bail out and show error to users.
Toast.makeText(mContext, R.string.toast_call_detail_error,
Toast.LENGTH_SHORT).show();
finish();
return;
}
// All calls are from the same number and same contact, so pick the first detail.
mDetails = details[0];
mNumber = TextUtils.isEmpty(mDetails.number) ? null : mDetails.number.toString();
mPostDialDigits = TextUtils.isEmpty(mDetails.postDialDigits)
? "" : mDetails.postDialDigits;
mDisplayNumber = mDetails.displayNumber;
final CharSequence callLocationOrType = getNumberTypeOrLocation(mDetails);
final CharSequence displayNumber;
if (!TextUtils.isEmpty(mDetails.postDialDigits)) {
displayNumber = mDetails.number + mDetails.postDialDigits;
} else {
displayNumber = mDetails.displayNumber;
}
final String displayNumberStr = mBidiFormatter.unicodeWrap(
displayNumber.toString(), TextDirectionHeuristics.LTR);
mDetails.nameDisplayOrder = mContactsPreferences.getDisplayOrder();
if (!TextUtils.isEmpty(mDetails.getPreferredName())) {
mCallerName.setText(mDetails.getPreferredName());
mCallerNumber.setText(callLocationOrType + " " + displayNumberStr);
} else {
mCallerName.setText(displayNumberStr);
if (!TextUtils.isEmpty(callLocationOrType)) {
mCallerNumber.setText(callLocationOrType);
mCallerNumber.setVisibility(View.VISIBLE);
} else {
mCallerNumber.setVisibility(View.GONE);
}
}
CharSequence accountLabel = PhoneAccountUtils.getAccountLabel(mContext,
mDetails.accountHandle);
CharSequence accountContentDescription =
PhoneCallDetails.createAccountLabelDescription(mResources, mDetails.viaNumber,
accountLabel);
if (!TextUtils.isEmpty(mDetails.viaNumber)) {
if (!TextUtils.isEmpty(accountLabel)) {
accountLabel = mResources.getString(R.string.call_log_via_number_phone_account,
accountLabel, mDetails.viaNumber);
} else {
accountLabel = mResources.getString(R.string.call_log_via_number,
mDetails.viaNumber);
}
}
if (!TextUtils.isEmpty(accountLabel)) {
mAccountLabel.setText(accountLabel);
mAccountLabel.setContentDescription(accountContentDescription);
mAccountLabel.setVisibility(View.VISIBLE);
} else {
mAccountLabel.setVisibility(View.GONE);
}
final boolean canPlaceCallsTo =
PhoneNumberUtil.canPlaceCallsTo(mNumber, mDetails.numberPresentation);
mCallButton.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
mCopyNumberActionItem.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
updateBlockActionItemVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
final boolean isSipNumber = PhoneNumberUtil.isSipNumber(mNumber);
final boolean isVoicemailNumber =
PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
final boolean showEditNumberBeforeCallAction =
canPlaceCallsTo && !isSipNumber && !isVoicemailNumber;
mEditBeforeCallActionItem.setVisibility(
showEditNumberBeforeCallAction ? View.VISIBLE : View.GONE);
final boolean showReportAction = mContactInfoHelper.canReportAsInvalid(
mDetails.sourceType, mDetails.objectId);
mReportActionItem.setVisibility(
showReportAction ? View.VISIBLE : View.GONE);
invalidateOptionsMenu();
mHistoryList.setAdapter(
new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
updateFilteredNumberChanges();
updateContactPhoto();
findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
}
/**
* Determines the location geocode text for a call, or the phone number type
* (if available).
*
* @param details The call details.
* @return The phone number type or location.
*/
private CharSequence getNumberTypeOrLocation(PhoneCallDetails details) {
if (!TextUtils.isEmpty(details.namePrimary)) {
return Phone.getTypeLabel(mResources, details.numberType,
details.numberLabel);
} else {
return details.geocode;
}
}
};
private Context mContext;
private ContactInfoHelper mContactInfoHelper;
private ContactsPreferences mContactsPreferences;
private CallTypeHelper mCallTypeHelper;
private ContactPhotoManager mContactPhotoManager;
private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
private LayoutInflater mInflater;
private Resources mResources;
private PhoneCallDetails mDetails;
protected String mNumber;
private Uri mVoicemailUri;
private String mPostDialDigits = "";
private String mDisplayNumber;
private ListView mHistoryList;
private QuickContactBadge mQuickContactBadge;
private TextView mCallerName;
private TextView mCallerNumber;
private TextView mAccountLabel;
private View mCallButton;
private TextView mBlockNumberActionItem;
private View mEditBeforeCallActionItem;
private View mReportActionItem;
private View mCopyNumberActionItem;
private Integer mBlockedNumberId;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mContext = this;
mResources = getResources();
mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
mContactsPreferences = new ContactsPreferences(mContext);
mCallTypeHelper = new CallTypeHelper(getResources());
mFilteredNumberAsyncQueryHandler =
new FilteredNumberAsyncQueryHandler(getContentResolver());
mVoicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.call_detail);
mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mHistoryList = (ListView) findViewById(R.id.history);
mHistoryList.addHeaderView(mInflater.inflate(R.layout.call_detail_header, null));
mHistoryList.addFooterView(
mInflater.inflate(R.layout.call_detail_footer, null), null, false);
mQuickContactBadge = (QuickContactBadge) findViewById(R.id.quick_contact_photo);
mQuickContactBadge.setOverlay(null);
if (CompatUtils.hasPrioritizedMimeType()) {
mQuickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
}
mCallerName = (TextView) findViewById(R.id.caller_name);
mCallerNumber = (TextView) findViewById(R.id.caller_number);
mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
mContactPhotoManager = ContactPhotoManager.getInstance(this);
mCallButton = findViewById(R.id.call_back_button);
mCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (TextUtils.isEmpty(mNumber)) {
return;
}
mContext.startActivity(
new CallIntentBuilder(getDialableNumber())
.setCallInitiationType(LogState.INITIATION_CALL_DETAILS)
.build());
}
});
mBlockNumberActionItem = (TextView) findViewById(R.id.call_detail_action_block);
updateBlockActionItemVisibility(View.VISIBLE);
mBlockNumberActionItem.setOnClickListener(this);
mEditBeforeCallActionItem = findViewById(R.id.call_detail_action_edit_before_call);
mEditBeforeCallActionItem.setOnClickListener(this);
mReportActionItem = findViewById(R.id.call_detail_action_report);
mReportActionItem.setOnClickListener(this);
mCopyNumberActionItem = findViewById(R.id.call_detail_action_copy);
mCopyNumberActionItem.setOnClickListener(this);
if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) {
closeSystemDialogs();
}
}
private void updateBlockActionItemVisibility(int visibility) {
if (!FilteredNumberCompat.canAttemptBlockOperations(mContext)) {
visibility = View.GONE;
}
mBlockNumberActionItem.setVisibility(visibility);
}
@Override
public void onResume() {
super.onResume();
mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
getCallDetails();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
}
return super.dispatchTouchEvent(ev);
}
public void getCallDetails() {
CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener);
}
/**
* Returns the list of URIs to show.
* <p>
* There are two ways the URIs can be provided to the activity: as the data on the intent, or as
* a list of ids in the call log added as an extra on the URI.
* <p>
* If both are available, the data on the intent takes precedence.
*/
private Uri[] getCallLogEntryUris() {
final Uri uri = getIntent().getData();
if (uri != null) {
// If there is a data on the intent, it takes precedence over the extra.
return new Uri[]{ uri };
}
final long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
final int numIds = ids == null ? 0 : ids.length;
final Uri[] uris = new Uri[numIds];
for (int index = 0; index < numIds; ++index) {
uris[index] = ContentUris.withAppendedId(
TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
}
return uris;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
final MenuItem deleteMenuItem = menu.add(
Menu.NONE,
R.id.call_detail_delete_menu_item,
Menu.NONE,
R.string.call_details_delete);
deleteMenuItem.setIcon(R.drawable.ic_delete_24dp);
deleteMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
deleteMenuItem.setOnMenuItemClickListener(this);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.call_detail_delete_menu_item) {
if (hasVoicemail()) {
CallLogAsyncTaskUtil.deleteVoicemail(
this, mVoicemailUri, mCallLogAsyncTaskListener);
} else {
final StringBuilder callIds = new StringBuilder();
for (Uri callUri : getCallLogEntryUris()) {
if (callIds.length() != 0) {
callIds.append(",");
}
callIds.append(ContentUris.parseId(callUri));
}
CallLogAsyncTaskUtil.deleteCalls(
this, callIds.toString(), mCallLogAsyncTaskListener);
}
}
return true;
}
@Override
public void onClick(View view) {
int resId = view.getId();
if (resId == R.id.call_detail_action_block) {
FilteredNumberCompat
.showBlockNumberDialogFlow(mContext.getContentResolver(), mBlockedNumberId,
mNumber, mDetails.countryIso, mDisplayNumber, R.id.call_detail,
getFragmentManager(), this);
} else if (resId == R.id.call_detail_action_copy) {
ClipboardUtils.copyText(mContext, null, mNumber, true);
} else if (resId == R.id.call_detail_action_edit_before_call) {
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
CallUtil.getCallUri(getDialableNumber()));
DialerUtils.startActivityWithErrorToast(mContext, dialIntent);
} else {
Log.wtf(TAG, "Unexpected onClick event from " + view);
}
}
@Override
public void onFilterNumberSuccess() {
Logger.logInteraction(InteractionEvent.BLOCK_NUMBER_CALL_DETAIL);
updateFilteredNumberChanges();
}
@Override
public void onUnfilterNumberSuccess() {
Logger.logInteraction(InteractionEvent.UNBLOCK_NUMBER_CALL_DETAIL);
updateFilteredNumberChanges();
}
@Override
public void onChangeFilteredNumberUndo() {
updateFilteredNumberChanges();
}
private void updateFilteredNumberChanges() {
if (mDetails == null ||
!FilteredNumbersUtil.canBlockNumber(this, mNumber, mDetails.countryIso)) {
return;
}
final boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
new OnCheckBlockedListener() {
@Override
public void onCheckComplete(Integer id) {
mBlockedNumberId = id;
updateBlockActionItem();
}
}, mNumber, mDetails.countryIso);
if (!success) {
updateBlockActionItem();
}
}
// Loads and displays the contact photo.
private void updateContactPhoto() {
if (mDetails == null) {
return;
}
final boolean isVoicemailNumber =
PhoneNumberUtil.isVoicemailNumber(mContext, mDetails.accountHandle, mNumber);
final boolean isBusiness = mContactInfoHelper.isBusiness(mDetails.sourceType);
int contactType = ContactPhotoManager.TYPE_DEFAULT;
if (isVoicemailNumber) {
contactType = ContactPhotoManager.TYPE_VOICEMAIL;
} else if (isBusiness) {
contactType = ContactPhotoManager.TYPE_BUSINESS;
}
final String displayName = TextUtils.isEmpty(mDetails.namePrimary)
? mDetails.displayNumber : mDetails.namePrimary.toString();
final String lookupKey = mDetails.contactUri == null
? null : UriUtils.getLookupKeyFromUri(mDetails.contactUri);
final DefaultImageRequest request =
new DefaultImageRequest(displayName, lookupKey, contactType, true /* isCircular */);
mQuickContactBadge.assignContactUri(mDetails.contactUri);
mQuickContactBadge.setContentDescription(
mResources.getString(R.string.description_contact_details, displayName));
mContactPhotoManager.loadDirectoryPhoto(mQuickContactBadge, mDetails.photoUri,
false /* darkTheme */, true /* isCircular */, request);
}
private void updateBlockActionItem() {
if (mBlockedNumberId == null) {
mBlockNumberActionItem.setText(R.string.action_block_number);
mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.ic_call_detail_block, 0, 0, 0);
} else {
mBlockNumberActionItem.setText(R.string.action_unblock_number);
mBlockNumberActionItem.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.ic_call_detail_unblock, 0, 0, 0);
}
}
private void closeSystemDialogs() {
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
private String getDialableNumber() {
return mNumber + mPostDialDigits;
}
@NeededForTesting
public boolean hasVoicemail() {
return mVoicemailUri != null;
}
}