Home | History | Annotate | Download | only in dialer
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dialer;
     18 
     19 import android.app.Activity;
     20 import android.content.ContentResolver;
     21 import android.content.ContentUris;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Resources;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.PowerManager;
     28 import android.provider.ContactsContract.CommonDataKinds.Phone;
     29 import android.provider.VoicemailContract.Voicemails;
     30 import android.telecom.PhoneAccount;
     31 import android.telecom.PhoneAccountHandle;
     32 import android.telephony.TelephonyManager;
     33 import android.text.BidiFormatter;
     34 import android.text.TextDirectionHeuristics;
     35 import android.text.TextUtils;
     36 import android.util.Log;
     37 import android.view.KeyEvent;
     38 import android.view.LayoutInflater;
     39 import android.view.Menu;
     40 import android.view.MenuItem;
     41 import android.view.View;
     42 import android.widget.LinearLayout;
     43 import android.widget.ListView;
     44 import android.widget.QuickContactBadge;
     45 import android.widget.TextView;
     46 import android.widget.Toast;
     47 
     48 import com.android.contacts.common.ContactPhotoManager;
     49 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
     50 import com.android.contacts.common.util.PermissionsUtil;
     51 import com.android.contacts.common.GeoUtil;
     52 import com.android.contacts.common.CallUtil;
     53 import com.android.dialer.calllog.CallDetailHistoryAdapter;
     54 import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
     55 import com.android.dialer.calllog.CallLogAsyncTaskUtil;
     56 import com.android.dialer.calllog.CallTypeHelper;
     57 import com.android.dialer.calllog.ContactInfo;
     58 import com.android.dialer.calllog.ContactInfoHelper;
     59 import com.android.dialer.calllog.PhoneAccountUtils;
     60 import com.android.dialer.calllog.PhoneNumberDisplayUtil;
     61 import com.android.dialer.util.DialerUtils;
     62 import com.android.dialer.util.IntentUtil;
     63 import com.android.dialer.util.PhoneNumberUtil;
     64 import com.android.dialer.util.TelecomUtil;
     65 
     66 import java.util.List;
     67 
     68 /**
     69  * Displays the details of a specific call log entry.
     70  * <p>
     71  * This activity can be either started with the URI of a single call log entry, or with the
     72  * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
     73  */
     74 public class CallDetailActivity extends Activity
     75         implements MenuItem.OnMenuItemClickListener {
     76     private static final String TAG = "CallDetail";
     77 
     78      /** A long array extra containing ids of call log entries to display. */
     79     public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
     80     /** If we are started with a voicemail, we'll find the uri to play with this extra. */
     81     public static final String EXTRA_VOICEMAIL_URI = "EXTRA_VOICEMAIL_URI";
     82     /** If the activity was triggered from a notification. */
     83     public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
     84 
     85     public static final String VOICEMAIL_FRAGMENT_TAG = "voicemail_fragment";
     86 
     87     private CallLogAsyncTaskListener mCallLogAsyncTaskListener = new CallLogAsyncTaskListener() {
     88         @Override
     89         public void onDeleteCall() {
     90             finish();
     91         }
     92 
     93         @Override
     94         public void onDeleteVoicemail() {
     95             finish();
     96         }
     97 
     98         @Override
     99         public void onGetCallDetails(PhoneCallDetails[] details) {
    100             if (details == null) {
    101                 // Somewhere went wrong: we're going to bail out and show error to users.
    102                 Toast.makeText(mContext, R.string.toast_call_detail_error,
    103                         Toast.LENGTH_SHORT).show();
    104                 finish();
    105                 return;
    106             }
    107 
    108             // We know that all calls are from the same number and the same contact, so pick the
    109             // first.
    110             PhoneCallDetails firstDetails = details[0];
    111             mNumber = TextUtils.isEmpty(firstDetails.number) ?
    112                     null : firstDetails.number.toString();
    113             final int numberPresentation = firstDetails.numberPresentation;
    114             final Uri contactUri = firstDetails.contactUri;
    115             final Uri photoUri = firstDetails.photoUri;
    116             final PhoneAccountHandle accountHandle = firstDetails.accountHandle;
    117 
    118             // Cache the details about the phone number.
    119             final boolean canPlaceCallsTo =
    120                     PhoneNumberUtil.canPlaceCallsTo(mNumber, numberPresentation);
    121             mIsVoicemailNumber =
    122                     PhoneNumberUtil.isVoicemailNumber(mContext, accountHandle, mNumber);
    123             final boolean isSipNumber = PhoneNumberUtil.isSipNumber(mNumber);
    124 
    125             final CharSequence callLocationOrType = getNumberTypeOrLocation(firstDetails);
    126 
    127             final CharSequence displayNumber = firstDetails.displayNumber;
    128             final String displayNumberStr = mBidiFormatter.unicodeWrap(
    129                     displayNumber.toString(), TextDirectionHeuristics.LTR);
    130 
    131             if (!TextUtils.isEmpty(firstDetails.name)) {
    132                 mCallerName.setText(firstDetails.name);
    133                 mCallerNumber.setText(callLocationOrType + " " + displayNumberStr);
    134             } else {
    135                 mCallerName.setText(displayNumberStr);
    136                 if (!TextUtils.isEmpty(callLocationOrType)) {
    137                     mCallerNumber.setText(callLocationOrType);
    138                     mCallerNumber.setVisibility(View.VISIBLE);
    139                 } else {
    140                     mCallerNumber.setVisibility(View.GONE);
    141                 }
    142             }
    143 
    144             mCallButton.setVisibility(canPlaceCallsTo ? View.VISIBLE : View.GONE);
    145 
    146             String accountLabel = PhoneAccountUtils.getAccountLabel(mContext, accountHandle);
    147             if (!TextUtils.isEmpty(accountLabel)) {
    148                 mAccountLabel.setText(accountLabel);
    149                 mAccountLabel.setVisibility(View.VISIBLE);
    150             } else {
    151                 mAccountLabel.setVisibility(View.GONE);
    152             }
    153 
    154             mHasEditNumberBeforeCallOption =
    155                     canPlaceCallsTo && !isSipNumber && !mIsVoicemailNumber;
    156             mHasReportMenuOption = mContactInfoHelper.canReportAsInvalid(
    157                     firstDetails.sourceType, firstDetails.objectId);
    158             invalidateOptionsMenu();
    159 
    160             ListView historyList = (ListView) findViewById(R.id.history);
    161             historyList.setAdapter(
    162                     new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
    163 
    164             String lookupKey = contactUri == null ? null
    165                     : ContactInfoHelper.getLookupKeyFromUri(contactUri);
    166 
    167             final boolean isBusiness = mContactInfoHelper.isBusiness(firstDetails.sourceType);
    168 
    169             final int contactType =
    170                     mIsVoicemailNumber ? ContactPhotoManager.TYPE_VOICEMAIL :
    171                     isBusiness ? ContactPhotoManager.TYPE_BUSINESS :
    172                     ContactPhotoManager.TYPE_DEFAULT;
    173 
    174             String nameForDefaultImage;
    175             if (TextUtils.isEmpty(firstDetails.name)) {
    176                 nameForDefaultImage = firstDetails.displayNumber;
    177             } else {
    178                 nameForDefaultImage = firstDetails.name.toString();
    179             }
    180 
    181             loadContactPhotos(
    182                     contactUri, photoUri, nameForDefaultImage, lookupKey, contactType);
    183             findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
    184         }
    185 
    186         /**
    187          * Determines the location geocode text for a call, or the phone number type
    188          * (if available).
    189          *
    190          * @param details The call details.
    191          * @return The phone number type or location.
    192          */
    193         private CharSequence getNumberTypeOrLocation(PhoneCallDetails details) {
    194             if (!TextUtils.isEmpty(details.name)) {
    195                 return Phone.getTypeLabel(mResources, details.numberType,
    196                         details.numberLabel);
    197             } else {
    198                 return details.geocode;
    199             }
    200         }
    201     };
    202 
    203     private Context mContext;
    204     private CallTypeHelper mCallTypeHelper;
    205     private QuickContactBadge mQuickContactBadge;
    206     private TextView mCallerName;
    207     private TextView mCallerNumber;
    208     private TextView mAccountLabel;
    209     private View mCallButton;
    210     private ContactInfoHelper mContactInfoHelper;
    211 
    212     protected String mNumber;
    213     private boolean mIsVoicemailNumber;
    214     private String mDefaultCountryIso;
    215 
    216     /* package */ LayoutInflater mInflater;
    217     /* package */ Resources mResources;
    218     /** Helper to load contact photos. */
    219     private ContactPhotoManager mContactPhotoManager;
    220 
    221     private Uri mVoicemailUri;
    222     private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
    223 
    224     /** Whether we should show "edit number before call" in the options menu. */
    225     private boolean mHasEditNumberBeforeCallOption;
    226     private boolean mHasReportMenuOption;
    227 
    228     @Override
    229     protected void onCreate(Bundle icicle) {
    230         super.onCreate(icicle);
    231 
    232         mContext = this;
    233 
    234         setContentView(R.layout.call_detail);
    235 
    236         mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    237         mResources = getResources();
    238 
    239         mCallTypeHelper = new CallTypeHelper(getResources());
    240 
    241         mVoicemailUri = getIntent().getParcelableExtra(EXTRA_VOICEMAIL_URI);
    242 
    243         mQuickContactBadge = (QuickContactBadge) findViewById(R.id.quick_contact_photo);
    244         mQuickContactBadge.setOverlay(null);
    245         mQuickContactBadge.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
    246         mCallerName = (TextView) findViewById(R.id.caller_name);
    247         mCallerNumber = (TextView) findViewById(R.id.caller_number);
    248         mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
    249         mDefaultCountryIso = GeoUtil.getCurrentCountryIso(this);
    250         mContactPhotoManager = ContactPhotoManager.getInstance(this);
    251 
    252         mCallButton = (View) findViewById(R.id.call_back_button);
    253         mCallButton.setOnClickListener(new View.OnClickListener() {
    254             @Override
    255             public void onClick(View view) {
    256                 mContext.startActivity(IntentUtil.getCallIntent(mNumber));
    257             }
    258         });
    259 
    260         mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
    261         getActionBar().setDisplayHomeAsUpEnabled(true);
    262 
    263         if (getIntent().getBooleanExtra(EXTRA_FROM_NOTIFICATION, false)) {
    264             closeSystemDialogs();
    265         }
    266     }
    267 
    268     @Override
    269     public void onResume() {
    270         super.onResume();
    271         getCallDetails();
    272     }
    273 
    274     public void getCallDetails() {
    275         CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener);
    276     }
    277 
    278     private boolean hasVoicemail() {
    279         return mVoicemailUri != null;
    280     }
    281 
    282     /**
    283      * Returns the list of URIs to show.
    284      * <p>
    285      * There are two ways the URIs can be provided to the activity: as the data on the intent, or as
    286      * a list of ids in the call log added as an extra on the URI.
    287      * <p>
    288      * If both are available, the data on the intent takes precedence.
    289      */
    290     private Uri[] getCallLogEntryUris() {
    291         final Uri uri = getIntent().getData();
    292         if (uri != null) {
    293             // If there is a data on the intent, it takes precedence over the extra.
    294             return new Uri[]{ uri };
    295         }
    296         final long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
    297         final int numIds = ids == null ? 0 : ids.length;
    298         final Uri[] uris = new Uri[numIds];
    299         for (int index = 0; index < numIds; ++index) {
    300             uris[index] = ContentUris.withAppendedId(
    301                     TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
    302         }
    303         return uris;
    304     }
    305 
    306     /** Load the contact photos and places them in the corresponding views. */
    307     private void loadContactPhotos(Uri contactUri, Uri photoUri, String displayName,
    308             String lookupKey, int contactType) {
    309 
    310         final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
    311                 contactType, true /* isCircular */);
    312 
    313         mQuickContactBadge.assignContactUri(contactUri);
    314         mQuickContactBadge.setContentDescription(
    315                 mResources.getString(R.string.description_contact_details, displayName));
    316 
    317         mContactPhotoManager.loadDirectoryPhoto(mQuickContactBadge, photoUri,
    318                 false /* darkTheme */, true /* isCircular */, request);
    319     }
    320 
    321     @Override
    322     public boolean onCreateOptionsMenu(Menu menu) {
    323         getMenuInflater().inflate(R.menu.call_details_options, menu);
    324         return super.onCreateOptionsMenu(menu);
    325     }
    326 
    327     @Override
    328     public boolean onPrepareOptionsMenu(Menu menu) {
    329         // This action deletes all elements in the group from the call log.
    330         // We don't have this action for voicemails, because you can just use the trash button.
    331         menu.findItem(R.id.menu_remove_from_call_log)
    332                 .setVisible(!hasVoicemail())
    333                 .setOnMenuItemClickListener(this);
    334         menu.findItem(R.id.menu_edit_number_before_call)
    335                 .setVisible(mHasEditNumberBeforeCallOption)
    336                 .setOnMenuItemClickListener(this);
    337         menu.findItem(R.id.menu_trash)
    338                 .setVisible(hasVoicemail())
    339                 .setOnMenuItemClickListener(this);
    340         menu.findItem(R.id.menu_report)
    341                 .setVisible(mHasReportMenuOption)
    342                 .setOnMenuItemClickListener(this);
    343         return super.onPrepareOptionsMenu(menu);
    344     }
    345 
    346     @Override
    347     public boolean onMenuItemClick(MenuItem item) {
    348         switch (item.getItemId()) {
    349             case R.id.menu_remove_from_call_log:
    350                 final StringBuilder callIds = new StringBuilder();
    351                 for (Uri callUri : getCallLogEntryUris()) {
    352                     if (callIds.length() != 0) {
    353                         callIds.append(",");
    354                     }
    355                     callIds.append(ContentUris.parseId(callUri));
    356                 }
    357                 CallLogAsyncTaskUtil.deleteCalls(
    358                         this, callIds.toString(), mCallLogAsyncTaskListener);
    359                 break;
    360             case R.id.menu_edit_number_before_call:
    361                 startActivity(new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(mNumber)));
    362                 break;
    363             case R.id.menu_trash:
    364                 CallLogAsyncTaskUtil.deleteVoicemail(
    365                         this, mVoicemailUri, mCallLogAsyncTaskListener);
    366                 break;
    367         }
    368         return true;
    369     }
    370 
    371     private void closeSystemDialogs() {
    372         sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
    373     }
    374 }
    375