From d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9 Mon Sep 17 00:00:00 2001 From: Eric Erfanian Date: Wed, 15 Mar 2017 14:41:07 -0700 Subject: Update Dialer source from latest green build. * Refactor voicemail component * Add new enriched calling components Test: treehugger, manual aosp testing Change-Id: I521a0f86327d4b42e14d93927c7d613044ed5942 --- .../android/dialer/calldetails/AndroidManifest.xml | 31 ++ .../dialer/calldetails/CallDetailsActivity.java | 130 ++++++ .../dialer/calldetails/CallDetailsAdapter.java | 97 +++++ .../calldetails/CallDetailsEntryViewHolder.java | 195 +++++++++ .../calldetails/CallDetailsFooterViewHolder.java | 67 ++++ .../calldetails/CallDetailsHeaderViewHolder.java | 125 ++++++ .../calldetails/nano/CallDetailsEntries.java | 440 +++++++++++++++++++++ .../res/drawable/multimedia_image_background.xml | 20 + .../res/layout/call_details_activity.xml | 37 ++ .../calldetails/res/layout/call_details_entry.xml | 73 ++++ .../calldetails/res/layout/call_details_footer.xml | 43 ++ .../calldetails/res/layout/contact_container.xml | 60 +++ .../calldetails/res/layout/ec_data_container.xml | 42 ++ .../calldetails/res/menu/call_details_menu.xml | 23 ++ .../dialer/calldetails/res/values/dimens.xml | 40 ++ .../dialer/calldetails/res/values/strings.xml | 42 ++ .../dialer/calldetails/res/values/styles.xml | 48 +++ 17 files changed, 1513 insertions(+) create mode 100644 java/com/android/dialer/calldetails/AndroidManifest.xml create mode 100644 java/com/android/dialer/calldetails/CallDetailsActivity.java create mode 100644 java/com/android/dialer/calldetails/CallDetailsAdapter.java create mode 100644 java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java create mode 100644 java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java create mode 100644 java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java create mode 100644 java/com/android/dialer/calldetails/nano/CallDetailsEntries.java create mode 100644 java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml create mode 100644 java/com/android/dialer/calldetails/res/layout/call_details_activity.xml create mode 100644 java/com/android/dialer/calldetails/res/layout/call_details_entry.xml create mode 100644 java/com/android/dialer/calldetails/res/layout/call_details_footer.xml create mode 100644 java/com/android/dialer/calldetails/res/layout/contact_container.xml create mode 100644 java/com/android/dialer/calldetails/res/layout/ec_data_container.xml create mode 100644 java/com/android/dialer/calldetails/res/menu/call_details_menu.xml create mode 100644 java/com/android/dialer/calldetails/res/values/dimens.xml create mode 100644 java/com/android/dialer/calldetails/res/values/strings.xml create mode 100644 java/com/android/dialer/calldetails/res/values/styles.xml (limited to 'java/com/android/dialer/calldetails') diff --git a/java/com/android/dialer/calldetails/AndroidManifest.xml b/java/com/android/dialer/calldetails/AndroidManifest.xml new file mode 100644 index 000000000..b71207ba2 --- /dev/null +++ b/java/com/android/dialer/calldetails/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java new file mode 100644 index 000000000..6070640a0 --- /dev/null +++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java @@ -0,0 +1,130 @@ +/* + * 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.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.provider.CallLog; +import android.provider.CallLog.Calls; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.MenuItem; +import android.widget.Toolbar; +import com.android.dialer.callcomposer.nano.CallComposerContact; +import com.android.dialer.calldetails.nano.CallDetailsEntries; +import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry; +import com.android.dialer.common.Assert; +import com.android.dialer.common.AsyncTaskExecutors; +import com.android.dialer.logging.Logger; +import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.protos.ProtoParsers; + +/** Displays the details of a specific call log entry. */ +public class CallDetailsActivity extends AppCompatActivity { + + private static final String EXTRA_CALL_DETAILS_ENTRIES = "call_details_entries"; + private static final String EXTRA_CONTACT = "contact"; + private static final String TASK_DELETE = "task_delete"; + + private CallDetailsEntry[] entries; + + public static Intent newInstance( + Context context, @NonNull CallDetailsEntries details, @NonNull CallComposerContact contact) { + Assert.isNotNull(details); + Assert.isNotNull(contact); + + Intent intent = new Intent(context, CallDetailsActivity.class); + ProtoParsers.put(intent, EXTRA_CONTACT, contact); + ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, details); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.call_details_activity); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setActionBar(toolbar); + toolbar.inflateMenu(R.menu.call_details_menu); + toolbar.setNavigationOnClickListener(v -> finish()); + onHandleIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + onHandleIntent(intent); + } + + private void onHandleIntent(Intent intent) { + Bundle arguments = intent.getExtras(); + CallComposerContact contact = + ProtoParsers.getFromInstanceState(arguments, EXTRA_CONTACT, new CallComposerContact()); + entries = + ProtoParsers.getFromInstanceState( + arguments, EXTRA_CALL_DETAILS_ENTRIES, new CallDetailsEntries()) + .entries; + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(new CallDetailsAdapter(this, contact, entries)); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.call_detail_delete_menu_item) { + Logger.get(this).logImpression(DialerImpression.Type.USER_DELETED_CALL_LOG_ITEM); + AsyncTaskExecutors.createAsyncTaskExecutor().submit(TASK_DELETE, new DeleteCallsTask()); + item.setEnabled(false); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Delete specified calls from the call log. */ + private class DeleteCallsTask extends AsyncTask { + + private final String callIds; + + DeleteCallsTask() { + StringBuilder callIds = new StringBuilder(); + for (CallDetailsEntry entry : entries) { + if (callIds.length() != 0) { + callIds.append(","); + } + callIds.append(entry.callId); + } + this.callIds = callIds.toString(); + } + + @Override + protected Void doInBackground(Void... params) { + getContentResolver() + .delete(Calls.CONTENT_URI, CallLog.Calls._ID + " IN (" + callIds + ")", null); + return null; + } + + @Override + public void onPostExecute(Void result) { + finish(); + } + } +} diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapter.java b/java/com/android/dialer/calldetails/CallDetailsAdapter.java new file mode 100644 index 000000000..954583077 --- /dev/null +++ b/java/com/android/dialer/calldetails/CallDetailsAdapter.java @@ -0,0 +1,97 @@ +/* + * 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.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import com.android.dialer.callcomposer.nano.CallComposerContact; +import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry; +import com.android.dialer.calllogutils.CallTypeHelper; +import com.android.dialer.common.Assert; + +/** Adapter for RecyclerView in {@link CallDetailsActivity}. */ +public class CallDetailsAdapter extends RecyclerView.Adapter { + + private static final int HEADER_VIEW_TYPE = 1; + private static final int CALL_ENTRY_VIEW_TYPE = 2; + private static final int FOOTER_VIEW_TYPE = 3; + + private final CallComposerContact contact; + private final CallDetailsEntry[] callDetailsEntries; + private final CallTypeHelper callTypeHelper; + + public CallDetailsAdapter( + Context context, CallComposerContact contact, CallDetailsEntry[] callDetailsEntries) { + this.contact = Assert.isNotNull(contact); + this.callDetailsEntries = Assert.isNotNull(callDetailsEntries); + callTypeHelper = new CallTypeHelper(context.getResources()); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + switch (viewType) { + case HEADER_VIEW_TYPE: + return new CallDetailsHeaderViewHolder( + inflater.inflate(R.layout.contact_container, parent, false)); + case CALL_ENTRY_VIEW_TYPE: + return new CallDetailsEntryViewHolder( + inflater.inflate(R.layout.call_details_entry, parent, false)); + case FOOTER_VIEW_TYPE: + return new CallDetailsFooterViewHolder( + inflater.inflate(R.layout.call_details_footer, parent, false)); + default: + Assert.fail("No ViewHolder available for viewType: " + viewType); + return null; + } + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + if (position == 0) { // Header + ((CallDetailsHeaderViewHolder) holder).updateContactInfo(contact); + } else if (position == getItemCount() - 1) { + ((CallDetailsFooterViewHolder) holder).setPhoneNumber(contact.number); + } else { + CallDetailsEntryViewHolder viewHolder = (CallDetailsEntryViewHolder) holder; + viewHolder.setCallDetails( + contact.number, + callDetailsEntries[position - 1], + callTypeHelper, + position != getItemCount() - 2); + } + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { // Header + return HEADER_VIEW_TYPE; + } else if (position == getItemCount() - 1) { + return FOOTER_VIEW_TYPE; + } else { + return CALL_ENTRY_VIEW_TYPE; + } + } + + @Override + public int getItemCount() { + return callDetailsEntries.length + 2; // Header + footer + } +} diff --git a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java new file mode 100644 index 000000000..b1a70af0c --- /dev/null +++ b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java @@ -0,0 +1,195 @@ +/* + * 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.res.Resources; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.provider.CallLog.Calls; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.text.TextUtils; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry; +import com.android.dialer.calllogutils.CallEntryFormatter; +import com.android.dialer.calllogutils.CallTypeHelper; +import com.android.dialer.common.LogUtil; +import com.android.dialer.compat.AppCompatConstants; +import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult; +import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type; +import com.android.dialer.util.CallUtil; +import com.android.dialer.util.DialerUtils; +import com.android.dialer.util.IntentUtil; + +/** ViewHolder for call entries in {@link CallDetailsActivity}. */ +public class CallDetailsEntryViewHolder extends ViewHolder { + + private final ImageView callTypeIcon; + private final TextView callTypeText; + private final TextView callTime; + private final TextView callDuration; + + private final View multimediaImageContainer; + private final View multimediaDetailsContainer; + private final View multimediaDivider; + + private final TextView multimediaDetails; + + private final ImageView multimediaImage; + + // TODO: Display this when location is stored - b/36160042 + @SuppressWarnings("unused") + private final TextView multimediaAttachmentsNumber; + + private final Context context; + + public CallDetailsEntryViewHolder(View container) { + super(container); + context = container.getContext(); + + callTypeIcon = (ImageView) container.findViewById(R.id.call_direction); + callTypeText = (TextView) container.findViewById(R.id.call_type); + callTime = (TextView) container.findViewById(R.id.call_time); + callDuration = (TextView) container.findViewById(R.id.call_duration); + + multimediaImageContainer = container.findViewById(R.id.multimedia_image_container); + multimediaDetailsContainer = container.findViewById(R.id.ec_container); + multimediaDivider = container.findViewById(R.id.divider); + multimediaDetails = (TextView) container.findViewById(R.id.multimedia_details); + multimediaImage = (ImageView) container.findViewById(R.id.multimedia_image); + multimediaAttachmentsNumber = + (TextView) container.findViewById(R.id.multimedia_attachments_number); + } + + void setCallDetails( + String number, + CallDetailsEntry entry, + CallTypeHelper callTypeHelper, + boolean showMultimediaDivider) { + int callType = entry.callType; + boolean isVideoCall = + (entry.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO + && CallUtil.isVideoEnabled(context); + boolean isPulledCall = + (entry.features & Calls.FEATURES_PULLED_EXTERNALLY) == Calls.FEATURES_PULLED_EXTERNALLY; + + Drawable callIcon = getIconForCallType(context.getResources(), callType); + int color = getColorForCallType(context, callType); + callIcon.setColorFilter(color, PorterDuff.Mode.MULTIPLY); + callTime.setTextColor(color); + callTypeIcon.setImageDrawable(callIcon); + + callTypeText.setText(callTypeHelper.getCallTypeText(callType, isVideoCall, isPulledCall)); + callTime.setText(CallEntryFormatter.formatDate(context, entry.date)); + if (CallTypeHelper.isMissedCallType(callType)) { + callDuration.setVisibility(View.GONE); + } else { + callDuration.setVisibility(View.VISIBLE); + callDuration.setText( + CallEntryFormatter.formatDurationAndDataUsage(context, entry.duration, entry.dataUsage)); + } + setMultimediaDetails(number, entry, showMultimediaDivider); + } + + private void setMultimediaDetails(String number, CallDetailsEntry entry, boolean showDivider) { + multimediaDivider.setVisibility(showDivider ? View.VISIBLE : View.GONE); + if (entry.historyResults == null || entry.historyResults.length <= 0) { + LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no data, hiding UI"); + multimediaDetailsContainer.setVisibility(View.GONE); + } else { + + // TODO: b/36158891 Add room for 2 pieces of enriched call data. It's possible + // to have both call composer data and post call data for a single call. + HistoryResult historyResult = entry.historyResults[0]; + multimediaDetailsContainer.setVisibility(View.VISIBLE); + multimediaDetailsContainer.setOnClickListener( + (v) -> { + DialerUtils.startActivityWithErrorToast(context, IntentUtil.getSendSmsIntent(number)); + }); + multimediaImageContainer.setClipToOutline(true); + + if (!TextUtils.isEmpty(historyResult.imageUri)) { + LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "setting image"); + multimediaImageContainer.setVisibility(View.VISIBLE); + multimediaImage.setImageURI(Uri.parse(historyResult.imageUri)); + multimediaDetails.setText( + isIncoming(historyResult) ? R.string.received_a_photo : R.string.sent_a_photo); + } else { + LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no image"); + } + + // Set text after image to overwrite the received/sent a photo text + if (!TextUtils.isEmpty(historyResult.text)) { + LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "showing text"); + multimediaDetails.setText( + context.getString(R.string.message_in_quotes, historyResult.text)); + } else { + LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no text"); + } + } + } + + private static boolean isIncoming(@NonNull HistoryResult historyResult) { + return historyResult.type == Type.INCOMING_POST_CALL + || historyResult.type == Type.INCOMING_CALL_COMPOSER; + } + + private static Drawable getIconForCallType(Resources resources, int callType) { + switch (callType) { + case AppCompatConstants.CALLS_OUTGOING_TYPE: + return resources.getDrawable(R.drawable.quantum_ic_call_made_white_24); + case AppCompatConstants.CALLS_BLOCKED_TYPE: + return resources.getDrawable(R.drawable.quantum_ic_block_white_24); + case AppCompatConstants.CALLS_INCOMING_TYPE: + case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE: + case AppCompatConstants.CALLS_REJECTED_TYPE: + return resources.getDrawable(R.drawable.quantum_ic_call_received_white_24); + case AppCompatConstants.CALLS_MISSED_TYPE: + default: + // It is possible for users to end up with calls with unknown call types in their + // call history, possibly due to 3rd party call log implementations (e.g. to + // distinguish between rejected and missed calls). Instead of crashing, just + // assume that all unknown call types are missed calls. + return resources.getDrawable(R.drawable.quantum_ic_call_missed_white_24); + } + } + + private static @ColorInt int getColorForCallType(Context context, int callType) { + switch (callType) { + case AppCompatConstants.CALLS_OUTGOING_TYPE: + case AppCompatConstants.CALLS_VOICEMAIL_TYPE: + case AppCompatConstants.CALLS_BLOCKED_TYPE: + case AppCompatConstants.CALLS_INCOMING_TYPE: + case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE: + case AppCompatConstants.CALLS_REJECTED_TYPE: + return ContextCompat.getColor(context, R.color.dialer_secondary_text_color); + case AppCompatConstants.CALLS_MISSED_TYPE: + default: + // It is possible for users to end up with calls with unknown call types in their + // call history, possibly due to 3rd party call log implementations (e.g. to + // distinguish between rejected and missed calls). Instead of crashing, just + // assume that all unknown call types are missed calls. + return ContextCompat.getColor(context, R.color.missed_call); + } + } +} diff --git a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java new file mode 100644 index 000000000..36662bab9 --- /dev/null +++ b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java @@ -0,0 +1,67 @@ +/* + * 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.Intent; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.View.OnClickListener; +import com.android.contacts.common.ClipboardUtils; +import com.android.dialer.common.Assert; +import com.android.dialer.logging.Logger; +import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.util.CallUtil; +import com.android.dialer.util.DialerUtils; + +/** ViewHolder container for {@link CallDetailsActivity} footer. */ +public class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder + implements OnClickListener { + + private final View copy; + private final View edit; + + private String number; + + public CallDetailsFooterViewHolder(View view) { + super(view); + copy = view.findViewById(R.id.call_detail_action_copy); + edit = view.findViewById(R.id.call_detail_action_edit_before_call); + + copy.setOnClickListener(this); + edit.setOnClickListener(this); + } + + public void setPhoneNumber(String number) { + this.number = number; + } + + @Override + public void onClick(View view) { + Context context = view.getContext(); + if (view == copy) { + Logger.get(context).logImpression(DialerImpression.Type.CALL_DETAILS_COPY_NUMBER); + ClipboardUtils.copyText(context, null, number, true); + } else if (view == edit) { + Logger.get(context).logImpression(DialerImpression.Type.CALL_DETAILS_EDIT_BEFORE_CALL); + Intent dialIntent = new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(number)); + DialerUtils.startActivityWithErrorToast(context, dialIntent); + } else { + Assert.fail("View on click not implemented: " + view); + } + } +} diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java new file mode 100644 index 000000000..1679c2baf --- /dev/null +++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java @@ -0,0 +1,125 @@ +/* + * 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.net.Uri; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.QuickContactBadge; +import android.widget.TextView; +import com.android.contacts.common.ContactPhotoManager; +import com.android.contacts.common.util.UriUtils; +import com.android.dialer.callcomposer.nano.CallComposerContact; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.callintent.nano.CallInitiationType; +import com.android.dialer.common.Assert; +import com.android.dialer.logging.Logger; +import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.util.DialerUtils; + +/** ViewHolder for Header/Contact in {@link CallDetailsActivity}. */ +public class CallDetailsHeaderViewHolder extends RecyclerView.ViewHolder + implements OnClickListener { + + private final View callBackButton; + private final TextView nameView; + private final TextView numberView; + private final QuickContactBadge contactPhoto; + private final Context context; + + private CallComposerContact contact; + + CallDetailsHeaderViewHolder(View container) { + super(container); + context = container.getContext(); + callBackButton = container.findViewById(R.id.call_back_button); + nameView = (TextView) container.findViewById(R.id.contact_name); + numberView = (TextView) container.findViewById(R.id.phone_number); + contactPhoto = (QuickContactBadge) container.findViewById(R.id.quick_contact_photo); + callBackButton.setOnClickListener(this); + } + + /** + * Populates the contact info fields based on the current contact information. Copied from {@link + * com.android.contacts.common.dialog.CallSubjectDialog}. + */ + public void updateContactInfo(CallComposerContact contact) { + this.contact = contact; + setPhoto( + contact.photoId, + Uri.parse(contact.photoUri), + Uri.parse(contact.contactUri), + contact.nameOrNumber, + contact.isBusiness); + + nameView.setText(contact.nameOrNumber); + if (!TextUtils.isEmpty(contact.numberLabel) && !TextUtils.isEmpty(contact.displayNumber)) { + numberView.setVisibility(View.VISIBLE); + String secondaryInfo = + context.getString( + com.android.contacts.common.R.string.call_subject_type_and_number, + contact.numberLabel, + contact.displayNumber); + numberView.setText(secondaryInfo); + } else { + numberView.setVisibility(View.GONE); + numberView.setText(null); + } + } + + /** + * Sets the photo on the quick contact galleryIcon. Copied from {@link + * com.android.contacts.common.dialog.CallSubjectDialog}. + */ + private void setPhoto( + long photoId, Uri photoUri, Uri contactUri, String displayName, boolean isBusiness) { + contactPhoto.assignContactUri(contactUri); + contactPhoto.setOverlay(null); + + int contactType = + isBusiness ? ContactPhotoManager.TYPE_BUSINESS : ContactPhotoManager.TYPE_DEFAULT; + String lookupKey = contactUri == null ? null : UriUtils.getLookupKeyFromUri(contactUri); + + ContactPhotoManager.DefaultImageRequest request = + new ContactPhotoManager.DefaultImageRequest( + displayName, lookupKey, contactType, true /* isCircular */); + + if (photoId == 0 && photoUri != null) { + contactPhoto.setImageDrawable( + context.getDrawable(R.drawable.product_logo_avatar_anonymous_color_120)); + } else { + ContactPhotoManager.getInstance(context) + .loadThumbnail( + contactPhoto, photoId, false /* darkTheme */, true /* isCircular */, request); + } + } + + @Override + public void onClick(View view) { + if (view == callBackButton) { + Logger.get(view.getContext()).logImpression(DialerImpression.Type.CALL_DETAILS_CALL_BACK); + DialerUtils.startActivityWithErrorToast( + view.getContext(), + new CallIntentBuilder(contact.number, CallInitiationType.Type.CALL_DETAILS).build()); + } else { + Assert.fail("View OnClickListener not implemented: " + view); + } + } +} diff --git a/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java b/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java new file mode 100644 index 000000000..aee8f3652 --- /dev/null +++ b/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java @@ -0,0 +1,440 @@ +/* + * 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. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! + +package com.android.dialer.calldetails.nano; + +/** This file is autogenerated, but javadoc required. */ +@SuppressWarnings("hiding") +public final class CallDetailsEntries + extends com.google.protobuf.nano.ExtendableMessageNano { + + /** This file is autogenerated, but javadoc required. */ + public static final class CallDetailsEntry + extends com.google.protobuf.nano.ExtendableMessageNano { + + private static volatile CallDetailsEntry[] _emptyArray; + public static CallDetailsEntry[] emptyArray() { + // Lazily initializes the empty array + if (_emptyArray == null) { + synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) { + if (_emptyArray == null) { + _emptyArray = new CallDetailsEntry[0]; + } + } + } + return _emptyArray; + } + + // optional int64 call_id = 1; + public long callId; + + // optional int32 call_type = 2; + public int callType; + + // optional int32 features = 3; + public int features; + + // optional int64 date = 4; + public long date; + + // optional int64 duration = 5; + public long duration; + + // optional int64 data_usage = 6; + public long dataUsage; + + // repeated .com.android.dialer.enrichedcall.historyquery.proto. + // HistoryResult history_results = 7; + public com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult[] historyResults; + + // @@protoc_insertion_point(class_scope:com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry) + + public CallDetailsEntry() { + clear(); + } + + public CallDetailsEntry clear() { + callId = 0L; + callType = 0; + features = 0; + date = 0L; + duration = 0L; + dataUsage = 0L; + historyResults = + com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.emptyArray(); + unknownFieldData = null; + cachedSize = -1; + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof CallDetailsEntry)) { + return false; + } + CallDetailsEntry other = (CallDetailsEntry) o; + if (this.callId != other.callId) { + return false; + } + if (this.callType != other.callType) { + return false; + } + if (this.features != other.features) { + return false; + } + if (this.date != other.date) { + return false; + } + if (this.duration != other.duration) { + return false; + } + if (this.dataUsage != other.dataUsage) { + return false; + } + if (!com.google.protobuf.nano.InternalNano.equals( + this.historyResults, other.historyResults)) { + return false; + } + if (unknownFieldData == null || unknownFieldData.isEmpty()) { + return other.unknownFieldData == null || other.unknownFieldData.isEmpty(); + } else { + return unknownFieldData.equals(other.unknownFieldData); + } + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + getClass().getName().hashCode(); + result = 31 * result + (int) (this.callId ^ (this.callId >>> 32)); + result = 31 * result + this.callType; + result = 31 * result + this.features; + result = 31 * result + (int) (this.date ^ (this.date >>> 32)); + result = 31 * result + (int) (this.duration ^ (this.duration >>> 32)); + result = 31 * result + (int) (this.dataUsage ^ (this.dataUsage >>> 32)); + result = 31 * result + com.google.protobuf.nano.InternalNano.hashCode(this.historyResults); + result = + 31 * result + + (unknownFieldData == null || unknownFieldData.isEmpty() + ? 0 + : unknownFieldData.hashCode()); + return result; + } + + @Override + public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output) + throws java.io.IOException { + if (this.callId != 0L) { + output.writeInt64(1, this.callId); + } + if (this.callType != 0) { + output.writeInt32(2, this.callType); + } + if (this.features != 0) { + output.writeInt32(3, this.features); + } + if (this.date != 0L) { + output.writeInt64(4, this.date); + } + if (this.duration != 0L) { + output.writeInt64(5, this.duration); + } + if (this.dataUsage != 0L) { + output.writeInt64(6, this.dataUsage); + } + if (this.historyResults != null && this.historyResults.length > 0) { + for (int i = 0; i < this.historyResults.length; i++) { + com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult element = + this.historyResults[i]; + if (element != null) { + output.writeMessage(7, element); + } + } + } + super.writeTo(output); + } + + @Override + protected int computeSerializedSize() { + int size = super.computeSerializedSize(); + if (this.callId != 0L) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(1, this.callId); + } + if (this.callType != 0) { + size += + com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(2, this.callType); + } + if (this.features != 0) { + size += + com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(3, this.features); + } + if (this.date != 0L) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(4, this.date); + } + if (this.duration != 0L) { + size += + com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(5, this.duration); + } + if (this.dataUsage != 0L) { + size += + com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(6, this.dataUsage); + } + if (this.historyResults != null && this.historyResults.length > 0) { + for (int i = 0; i < this.historyResults.length; i++) { + com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult element = + this.historyResults[i]; + if (element != null) { + size += + com.google.protobuf.nano.CodedOutputByteBufferNano.computeMessageSize(7, element); + } + } + } + return size; + } + + @Override + public CallDetailsEntry mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + return this; + default: + { + if (!super.storeUnknownField(input, tag)) { + return this; + } + break; + } + case 8: + { + this.callId = input.readInt64(); + break; + } + case 16: + { + this.callType = input.readInt32(); + break; + } + case 24: + { + this.features = input.readInt32(); + break; + } + case 32: + { + this.date = input.readInt64(); + break; + } + case 40: + { + this.duration = input.readInt64(); + break; + } + case 48: + { + this.dataUsage = input.readInt64(); + break; + } + case 58: + { + int arrayLength = + com.google.protobuf.nano.WireFormatNano.getRepeatedFieldArrayLength(input, 58); + int i = this.historyResults == null ? 0 : this.historyResults.length; + com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult[] newArray = + new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult + [i + arrayLength]; + if (i != 0) { + java.lang.System.arraycopy(this.historyResults, 0, newArray, 0, i); + } + for (; i < newArray.length - 1; i++) { + newArray[i] = + new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult(); + input.readMessage(newArray[i]); + input.readTag(); + } + // Last one without readTag. + newArray[i] = + new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult(); + input.readMessage(newArray[i]); + this.historyResults = newArray; + break; + } + } + } + } + + public static CallDetailsEntry parseFrom(byte[] data) + throws com.google.protobuf.nano.InvalidProtocolBufferNanoException { + return com.google.protobuf.nano.MessageNano.mergeFrom(new CallDetailsEntry(), data); + } + + public static CallDetailsEntry parseFrom( + com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException { + return new CallDetailsEntry().mergeFrom(input); + } + } + + private static volatile CallDetailsEntries[] _emptyArray; + public static CallDetailsEntries[] emptyArray() { + // Lazily initializes the empty array + if (_emptyArray == null) { + synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) { + if (_emptyArray == null) { + _emptyArray = new CallDetailsEntries[0]; + } + } + } + return _emptyArray; + } + + // repeated .com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry entries = 1; + public com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry[] entries; + + // @@protoc_insertion_point(class_scope:com.android.dialer.calldetails.CallDetailsEntries) + + public CallDetailsEntries() { + clear(); + } + + public CallDetailsEntries clear() { + entries = com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry.emptyArray(); + unknownFieldData = null; + cachedSize = -1; + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof CallDetailsEntries)) { + return false; + } + CallDetailsEntries other = (CallDetailsEntries) o; + if (!com.google.protobuf.nano.InternalNano.equals(this.entries, other.entries)) { + return false; + } + if (unknownFieldData == null || unknownFieldData.isEmpty()) { + return other.unknownFieldData == null || other.unknownFieldData.isEmpty(); + } else { + return unknownFieldData.equals(other.unknownFieldData); + } + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + getClass().getName().hashCode(); + result = 31 * result + com.google.protobuf.nano.InternalNano.hashCode(this.entries); + result = + 31 * result + + (unknownFieldData == null || unknownFieldData.isEmpty() + ? 0 + : unknownFieldData.hashCode()); + return result; + } + + @Override + public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output) + throws java.io.IOException { + if (this.entries != null && this.entries.length > 0) { + for (int i = 0; i < this.entries.length; i++) { + com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry element = + this.entries[i]; + if (element != null) { + output.writeMessage(1, element); + } + } + } + super.writeTo(output); + } + + @Override + protected int computeSerializedSize() { + int size = super.computeSerializedSize(); + if (this.entries != null && this.entries.length > 0) { + for (int i = 0; i < this.entries.length; i++) { + com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry element = + this.entries[i]; + if (element != null) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeMessageSize(1, element); + } + } + } + return size; + } + + @Override + public CallDetailsEntries mergeFrom(com.google.protobuf.nano.CodedInputByteBufferNano input) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + return this; + default: + { + if (!super.storeUnknownField(input, tag)) { + return this; + } + break; + } + case 10: + { + int arrayLength = + com.google.protobuf.nano.WireFormatNano.getRepeatedFieldArrayLength(input, 10); + int i = this.entries == null ? 0 : this.entries.length; + com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry[] newArray = + new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry + [i + arrayLength]; + if (i != 0) { + java.lang.System.arraycopy(this.entries, 0, newArray, 0, i); + } + for (; i < newArray.length - 1; i++) { + newArray[i] = + new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry(); + input.readMessage(newArray[i]); + input.readTag(); + } + // Last one without readTag. + newArray[i] = + new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry(); + input.readMessage(newArray[i]); + this.entries = newArray; + break; + } + } + } + } + + public static CallDetailsEntries parseFrom(byte[] data) + throws com.google.protobuf.nano.InvalidProtocolBufferNanoException { + return com.google.protobuf.nano.MessageNano.mergeFrom(new CallDetailsEntries(), data); + } + + public static CallDetailsEntries parseFrom( + com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException { + return new CallDetailsEntries().mergeFrom(input); + } +} diff --git a/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml b/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml new file mode 100644 index 000000000..421bdbfee --- /dev/null +++ b/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml b/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml new file mode 100644 index 000000000..038a8745e --- /dev/null +++ b/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml new file mode 100644 index 000000000..7f8bb8087 --- /dev/null +++ b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_footer.xml b/java/com/android/dialer/calldetails/res/layout/call_details_footer.xml new file mode 100644 index 000000000..885cb0989 --- /dev/null +++ b/java/com/android/dialer/calldetails/res/layout/call_details_footer.xml @@ -0,0 +1,43 @@ + + + + + + + + + + diff --git a/java/com/android/dialer/calldetails/res/layout/contact_container.xml b/java/com/android/dialer/calldetails/res/layout/contact_container.xml new file mode 100644 index 000000000..95fe189b2 --- /dev/null +++ b/java/com/android/dialer/calldetails/res/layout/contact_container.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml b/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml new file mode 100644 index 000000000..5ad7912fa --- /dev/null +++ b/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml b/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml new file mode 100644 index 000000000..c2d1032da --- /dev/null +++ b/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/values/dimens.xml b/java/com/android/dialer/calldetails/res/values/dimens.xml new file mode 100644 index 000000000..b1a8f1c8e --- /dev/null +++ b/java/com/android/dialer/calldetails/res/values/dimens.xml @@ -0,0 +1,40 @@ + + + + 2dp + 16sp + 14sp + + + 16dp + 40dp + 16dp + 24dp + + + 24dp + 16dp + 14dp + 32dp + + + 12sp + 48dp + 72dp + 40dp + 8dp + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/values/strings.xml b/java/com/android/dialer/calldetails/res/values/strings.xml new file mode 100644 index 000000000..8a7cc4cfc --- /dev/null +++ b/java/com/android/dialer/calldetails/res/values/strings.xml @@ -0,0 +1,42 @@ + + + + + Call details + + + Delete + + + Copy number + + + Edit number before call + + + Call + + + Sent a photo + + + Received a photo + + + \"%1$s\" + diff --git a/java/com/android/dialer/calldetails/res/values/styles.xml b/java/com/android/dialer/calldetails/res/values/styles.xml new file mode 100644 index 000000000..4fffe1afb --- /dev/null +++ b/java/com/android/dialer/calldetails/res/values/styles.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3