From c1b7d0ae8cdfc0f0dddb5c2bb45c8239ffc6a264 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Wed, 15 Apr 2015 17:23:27 -0700 Subject: Move call log actions into expandable view. This isn't the prettiest thing ever, but with this the "framework" of all of the changes for the new visual style of the call logs is in place. From here on out, the remainder of tasks should be unblocked and relatively independent. + Rename ...ItemViews to ...ItemViewHolder. This probably wasn't completely necessary, but is more proper given the new architecture. + ViewHolder is now officially where most of the independent UI logic for a single call log list item should live. This changelist moves further in that direction by storing references and helpers in it, to lessen what must be passed in from the adapter. + Split out the call action from the rest of the actions, since it has a special treatment on the card. + Convert text action buttons into a vertical stack. + Consolidate action stylings into a single style, for simplicity. + Miscellaneous style and string changes to put things in a better state. This included tweaking some of the (ripple) backgrounds. + Update tests, according to changes. Bug: 19372817 Change-Id: Ic923c0bcbbc1c153952131d0c772df9e9589fb03 --- src/com/android/dialer/calllog/CallLogAdapter.java | 49 +-- .../dialer/calllog/CallLogListItemHelper.java | 8 +- .../dialer/calllog/CallLogListItemViewHolder.java | 438 +++++++++++++++++++++ .../dialer/calllog/CallLogListItemViews.java | 390 ------------------ 4 files changed, 455 insertions(+), 430 deletions(-) create mode 100644 src/com/android/dialer/calllog/CallLogListItemViewHolder.java delete mode 100644 src/com/android/dialer/calllog/CallLogListItemViews.java (limited to 'src') diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index 07cd215e9..cc1a4a632 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -153,8 +153,7 @@ public class CallLogAdapter extends GroupingListAdapter private final View.OnClickListener mExpandCollapseListener = new View.OnClickListener() { @Override public void onClick(View v) { - final View callLogItem = (View) v.getParent().getParent(); - handleRowExpanded(callLogItem, false /* forceExpand */); + handleRowExpanded(v, false /* forceExpand */); } }; @@ -266,15 +265,15 @@ public class CallLogAdapter extends GroupingListAdapter LayoutInflater inflater = LayoutInflater.from(mContext); View view = inflater.inflate(R.layout.call_log_list_item, parent, false); - // Get the views to bind to and cache them. - CallLogListItemViews views = CallLogListItemViews.fromView(mContext, view); - view.setTag(views); - - // Set text height to false on the TextViews so they don't have extra padding. - views.phoneCallDetailsViews.nameView.setElegantTextHeight(false); - views.phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false); + CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create( + view, + mContext, + mActionListener, + mPhoneNumberUtilsWrapper, + mCallLogViewsHelper); + viewHolder.primaryActionView.setTag(viewHolder); - return (CallLogListItemViews) view.getTag(); + return viewHolder; } /** @@ -292,7 +291,7 @@ public class CallLogAdapter extends GroupingListAdapter } int count = getGroupSize(position); - CallLogListItemViews views = (CallLogListItemViews) viewHolder; + CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder; views.rootView.setAccessibilityDelegate(mAccessibilityDelegate); // Default case: an item in the call log. @@ -378,12 +377,7 @@ public class CallLogAdapter extends GroupingListAdapter // Restore expansion state of the row on rebind. Inflate the actions ViewStub if required, // and set its visibility state accordingly. - views.expandOrCollapseActions( - isExpanded(rowId), - mOnReportButtonClickListener, - mActionListener, - mPhoneNumberUtilsWrapper, - mCallLogViewsHelper); + views.showActions(isExpanded(rowId), mOnReportButtonClickListener); if (TextUtils.isEmpty(name)) { details = new PhoneCallDetails(number, numberPresentation, formattedNumber, countryIso, @@ -409,6 +403,8 @@ public class CallLogAdapter extends GroupingListAdapter mContactInfoHelper.isBusiness(info.sourceType)); views.quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE); + views.updateCallButton(); + // Listen for the first draw if (mViewTreeObserver == null) { mViewTreeObserver = views.rootView.getViewTreeObserver(); @@ -603,28 +599,13 @@ public class CallLogAdapter extends GroupingListAdapter * of its previous state */ private void handleRowExpanded(View view, boolean forceExpand) { - final CallLogListItemViews views = (CallLogListItemViews) view.getTag(); + final CallLogListItemViewHolder views = (CallLogListItemViewHolder) view.getTag(); if (forceExpand && isExpanded(views.rowId)) { return; } - // Hide or show the actions view. boolean expanded = toggleExpansion(views.rowId); - expandItem(views, expanded); - } - - /** - * @param views The view holder for the item to expand or collapse. - * @param expand {@code true} to expand the item, {@code false} otherwise. - */ - public void expandItem(CallLogListItemViews views, boolean expand) { - // Trigger loading of the viewstub and visual expand or collapse. - views.expandOrCollapseActions( - expand, - mOnReportButtonClickListener, - mActionListener, - mPhoneNumberUtilsWrapper, - mCallLogViewsHelper); + views.showActions(expanded, mOnReportButtonClickListener); } } diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java index 77ad333d9..0e94ef402 100644 --- a/src/com/android/dialer/calllog/CallLogListItemHelper.java +++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java @@ -62,7 +62,7 @@ import com.android.dialer.R; * @param details the details of a phone call needed to fill in the data */ public void setPhoneCallDetails( - Context context, CallLogListItemViews views, PhoneCallDetails details) { + Context context, CallLogListItemViewHolder views, PhoneCallDetails details) { mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details); // Set the accessibility text for the contact badge @@ -81,7 +81,7 @@ import com.android.dialer.R; * * @param views The views associated with the current call log entry. */ - public void setActionContentDescriptions(CallLogListItemViews views) { + public void setActionContentDescriptions(CallLogListItemViewHolder views) { if (views.nameOrNumber == null) { Log.e(TAG, "setActionContentDescriptions; name or number is null."); } @@ -90,10 +90,6 @@ import com.android.dialer.R; // Although we don't expect a null name or number, it is best to protect against it. CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber; - views.callBackButtonView.setContentDescription( - TextUtils.expandTemplate( - mResources.getString(R.string.description_call_back_action), nameOrNumber)); - views.videoCallButtonView.setContentDescription( TextUtils.expandTemplate( mResources.getString(R.string.description_video_call_action), diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java new file mode 100644 index 000000000..4b208f14a --- /dev/null +++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2011 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.calllog; + +import android.content.Context; +import android.content.res.Resources; +import android.content.Intent; +import android.net.Uri; +import android.provider.CallLog.Calls; +import android.support.v7.widget.CardView; +import android.support.v7.widget.RecyclerView; +import android.telecom.PhoneAccountHandle; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStub; +import android.view.ViewTreeObserver; +import android.widget.QuickContactBadge; +import android.widget.TextView; + +import com.android.contacts.common.CallUtil; +import com.android.contacts.common.ContactPhotoManager; +import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; +import com.android.contacts.common.testing.NeededForTesting; +import com.android.dialer.PhoneCallDetailsHelper; +import com.android.dialer.PhoneCallDetailsViews; +import com.android.dialer.R; + +/** + * This is an object containing references to views contained by the call log list item. This + * improves performance by reducing the frequency with which we need to find views by IDs. + * + * This object also contains UI logic pertaining to the view, to isolate it from the CallLogAdapter. + */ +public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder { + + /** The root view of the call log list item */ + public final View rootView; + /** The quick contact badge for the contact. */ + public final QuickContactBadge quickContactView; + /** The primary action view of the entry. */ + public final View primaryActionView; + /** The details of the phone call. */ + public final PhoneCallDetailsViews phoneCallDetailsViews; + /** The text of the header for a day grouping. */ + public final TextView dayGroupHeader; + /** The view containing the details for the call log row, including the action buttons. */ + public final CardView callLogEntryView; + /** The actionable view which places a call to the number corresponding to the call log row. */ + public final View callActionView; + /** The view containing call log item actions. Null until the ViewStub is inflated. */ + public View actionsView; + /** The "video call" action button - assigned only when the action section is expanded. */ + public TextView videoCallButtonView; + /** The "voicemail" action button - assigned only when the action section is expanded. */ + public TextView voicemailButtonView; + /** The "details" action button - assigned only when the action section is expanded. */ + public TextView detailsButtonView; + /** The "report" action button. */ + public TextView reportButtonView; + + /** + * The row Id for the first call associated with the call log entry. Used as a key for the + * map used to track which call log entries have the action button section expanded. + */ + public long rowId; + + /** + * The call Ids for the calls represented by the current call log entry. Used when the user + * deletes a call log entry. + */ + public long[] callIds; + + /** + * The callable phone number for the current call log entry. Cached here as the call back + * intent is set only when the actions ViewStub is inflated. + */ + public String number; + + /** + * The phone number presentation for the current call log entry. Cached here as the call back + * intent is set only when the actions ViewStub is inflated. + */ + public int numberPresentation; + + /** + * The type of call for the current call log entry. Cached here as the call back + * intent is set only when the actions ViewStub is inflated. + */ + public int callType; + + /** + * The account for the current call log entry. Cached here as the call back + * intent is set only when the actions ViewStub is inflated. + */ + public PhoneAccountHandle accountHandle; + + /** + * If the call has an associated voicemail message, the URI of the voicemail message for + * playback. Cached here as the voicemail intent is only set when the actions ViewStub is + * inflated. + */ + public String voicemailUri; + + /** + * The name or number associated with the call. Cached here for use when setting content + * descriptions on buttons in the actions ViewStub when it is inflated. + */ + public CharSequence nameOrNumber; + + /** + * Whether or not the item has been reported by user as incorrect. + */ + public boolean reported; + + /** + * Whether or not the contact info can be marked as invalid from the source where + * it was obtained. + */ + public boolean canBeReportedAsInvalid; + + private static final int VOICEMAIL_TRANSCRIPTION_MAX_LINES = 10; + + private final Context mContext; + private final View.OnClickListener mActionListener; + private final PhoneNumberUtilsWrapper mPhoneNumberUtilsWrapper; + private final CallLogListItemHelper mCallLogListItemHelper; + + private final int mPhotoSize; + + private CallLogListItemViewHolder( + Context context, + View.OnClickListener actionListener, + PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, + CallLogListItemHelper callLogListItemHelper, + View rootView, + QuickContactBadge quickContactView, + View primaryActionView, + PhoneCallDetailsViews phoneCallDetailsViews, + CardView callLogEntryView, + TextView dayGroupHeader, + View callActionView) { + super(rootView); + + mContext = context; + mActionListener = actionListener; + mPhoneNumberUtilsWrapper = phoneNumberUtilsWrapper; + mCallLogListItemHelper = callLogListItemHelper; + + this.rootView = rootView; + this.quickContactView = quickContactView; + this.primaryActionView = primaryActionView; + this.phoneCallDetailsViews = phoneCallDetailsViews; + this.callLogEntryView = callLogEntryView; + this.dayGroupHeader = dayGroupHeader; + this.callActionView = callActionView; + + Resources resources = mContext.getResources(); + mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size); + + // Set text height to false on the TextViews so they don't have extra padding. + phoneCallDetailsViews.nameView.setElegantTextHeight(false); + phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false); + + if (callActionView != null) { + callActionView.setOnClickListener(mActionListener); + } + } + + public static CallLogListItemViewHolder create( + View view, + Context context, + View.OnClickListener actionListener, + PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, + CallLogListItemHelper callLogListItemHelper) { + + return new CallLogListItemViewHolder( + context, + actionListener, + phoneNumberUtilsWrapper, + callLogListItemHelper, + view, + (QuickContactBadge) view.findViewById(R.id.quick_contact_photo), + view.findViewById(R.id.primary_action_view), + PhoneCallDetailsViews.fromView(view), + (CardView) view.findViewById(R.id.call_log_row), + (TextView) view.findViewById(R.id.call_log_day_group_label), + view.findViewById(R.id.call_icon)); + } + + /** + * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not + * inflated during initial binding, so click handlers, tags and accessibility text must be set + * here, if necessary. + * + * @param callLogItem The call log list item view. + */ + public void inflateActionViewStub( + final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener) { + ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub); + if (stub != null) { + actionsView = (ViewGroup) stub.inflate(); + } + + if (videoCallButtonView == null) { + videoCallButtonView = (TextView) actionsView.findViewById(R.id.video_call_action); + } + + if (voicemailButtonView == null) { + voicemailButtonView = (TextView) actionsView.findViewById(R.id.voicemail_action); + } + + if (detailsButtonView == null) { + detailsButtonView = (TextView) actionsView.findViewById(R.id.details_action); + } + + if (reportButtonView == null) { + reportButtonView = (TextView) actionsView.findViewById(R.id.report_action); + reportButtonView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onReportButtonClickListener != null) { + onReportButtonClickListener.onReportButtonClick(number); + } + } + }); + } + + bindActionButtons(); + } + + public void updateCallButton() { + boolean canPlaceCallToNumber = + PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation); + + if (canPlaceCallToNumber) { + boolean isVoicemailNumber = + mPhoneNumberUtilsWrapper.isVoicemailNumber(accountHandle, number); + if (isVoicemailNumber) { + // Make a general call to voicemail to ensure that if there are multiple accounts + // it does not call the voicemail number of a specific phone account. + callActionView.setTag(IntentProvider.getReturnVoicemailCallIntentProvider()); + } else { + callActionView.setTag(IntentProvider.getReturnCallIntentProvider(number)); + } + + if (nameOrNumber != null) { + callActionView.setContentDescription(TextUtils.expandTemplate( + mContext.getString(R.string.description_call_action), + nameOrNumber)); + } else { + callActionView.setContentDescription( + mContext.getString(R.string.description_call_log_call_action)); + } + + callActionView.setVisibility(View.VISIBLE); + } else { + callActionView.setTag(null); + callActionView.setVisibility(View.GONE); + } + } + + /** + * Binds text titles, click handlers and intents to the voicemail, details and callback action + * buttons. + */ + private void bindActionButtons() { + boolean canPlaceCallToNumber = + PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation); + + // If one of the calls had video capabilities, show the video call button. + if (CallUtil.isVideoEnabled(mContext) && canPlaceCallToNumber && + phoneCallDetailsViews.callTypeIcons.isVideoShown()) { + videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number)); + videoCallButtonView.setVisibility(View.VISIBLE); + videoCallButtonView.setOnClickListener(mActionListener); + } else { + videoCallButtonView.setTag(null); + videoCallButtonView.setVisibility(View.GONE); + } + + // For voicemail calls, show the "VOICEMAIL" action button; hide otherwise. + if (callType == Calls.VOICEMAIL_TYPE) { + voicemailButtonView.setOnClickListener(mActionListener); + voicemailButtonView.setTag( + IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri)); + voicemailButtonView.setVisibility(View.VISIBLE); + + detailsButtonView.setVisibility(View.GONE); + } else { + voicemailButtonView.setTag(null); + voicemailButtonView.setVisibility(View.GONE); + + detailsButtonView.setOnClickListener(mActionListener); + detailsButtonView.setTag( + IntentProvider.getCallDetailIntentProvider(rowId, callIds, null)); + + if (canBeReportedAsInvalid && !reported) { + reportButtonView.setVisibility(View.VISIBLE); + } else { + reportButtonView.setVisibility(View.GONE); + } + } + + mCallLogListItemHelper.setActionContentDescriptions(this); + } + + /** + * Show or hide the action views, such as voicemail, details, and add contact. + * + * If the action views have never been shown yet for this view, inflate the view stub. + */ + public void showActions(boolean show, + final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener) { + expandVoicemailTranscriptionView(show); + + if (show) { + // Inflate the view stub if necessary, and wire up the event handlers. + inflateActionViewStub(onReportButtonClickListener); + + actionsView.setVisibility(View.VISIBLE); + actionsView.setAlpha(1.0f); + } else { + // When recycling a view, it is possible the actionsView ViewStub was previously + // inflated so we should hide it in this case. + if (actionsView != null) { + actionsView.setVisibility(View.GONE); + } + } + + if (actionsView != null) { + final ViewTreeObserver observer = callLogEntryView.getViewTreeObserver(); + observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (observer.isAlive()) { + observer.removeOnPreDrawListener(this); + } + + // TODO: Animate, instead of immediately resizing. + int currentHeight = primaryActionView.getHeight(); + int actionsHeight = actionsView.getVisibility() == View.VISIBLE + ? actionsView.getHeight() : 0; + callLogEntryView.getLayoutParams().height = currentHeight + actionsHeight; + callLogEntryView.requestLayout(); + + return false; + } + }); + } + } + + public void expandVoicemailTranscriptionView(boolean isExpanded) { + if (callType != Calls.VOICEMAIL_TYPE) { + return; + } + + final TextView view = phoneCallDetailsViews.voicemailTranscriptionView; + if (TextUtils.isEmpty(view.getText())) { + return; + } + view.setMaxLines(isExpanded ? VOICEMAIL_TRANSCRIPTION_MAX_LINES : 1); + view.setSingleLine(!isExpanded); + } + + public void setPhoto(long photoId, Uri photoUri, Uri contactUri, String displayName, + boolean isVoicemail, boolean isBusiness) { + quickContactView.assignContactUri(contactUri); + quickContactView.setOverlay(null); + + int contactType = ContactPhotoManager.TYPE_DEFAULT; + if (isVoicemail) { + contactType = ContactPhotoManager.TYPE_VOICEMAIL; + } else if (isBusiness) { + contactType = ContactPhotoManager.TYPE_BUSINESS; + } + + String lookupKey = null; + if (contactUri != null) { + lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri); + } + + DefaultImageRequest request = new DefaultImageRequest( + displayName, lookupKey, contactType, true /* isCircular */); + + if (photoId == 0 && photoUri != null) { + ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, photoUri, + mPhotoSize, false /* darkTheme */, true /* isCircular */, request); + } else { + ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, photoId, + false /* darkTheme */, true /* isCircular */, request); + } + } + + @NeededForTesting + public static CallLogListItemViewHolder createForTest(Context context) { + Resources resources = context.getResources(); + PhoneNumberDisplayHelper phoneNumberHelper = + new PhoneNumberDisplayHelper(context, resources); + PhoneNumberUtilsWrapper phoneNumberUtilsWrapper = new PhoneNumberUtilsWrapper(context); + PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper( + context, resources, phoneNumberUtilsWrapper); + + CallLogListItemViewHolder viewHolder = new CallLogListItemViewHolder( + context, + null /* actionListener */, + phoneNumberUtilsWrapper, + new CallLogListItemHelper( + phoneCallDetailsHelper, phoneNumberHelper, resources), + new View(context), + new QuickContactBadge(context), + new View(context), + PhoneCallDetailsViews.createForTest(context), + new CardView(context), + new TextView(context), + new View(context)); + viewHolder.voicemailButtonView = new TextView(context); + viewHolder.detailsButtonView = new TextView(context); + viewHolder.reportButtonView = new TextView(context); + viewHolder.actionsView = new View(context); + + return viewHolder; + } +} diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java deleted file mode 100644 index 4d4343692..000000000 --- a/src/com/android/dialer/calllog/CallLogListItemViews.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2011 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.calllog; - -import android.content.Context; -import android.content.res.Resources; -import android.net.Uri; -import android.provider.CallLog.Calls; -import android.support.v7.widget.CardView; -import android.support.v7.widget.RecyclerView; -import android.telecom.PhoneAccountHandle; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewStub; -import android.widget.QuickContactBadge; -import android.widget.TextView; - -import com.android.contacts.common.CallUtil; -import com.android.contacts.common.ContactPhotoManager; -import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; -import com.android.contacts.common.testing.NeededForTesting; -import com.android.dialer.PhoneCallDetailsViews; -import com.android.dialer.R; - -/** - * This is an object containing the various views within a call log entry. It contains values - * pointing to views contained by a call log list item view, so that we can improve performance - * by reducing the frequency with which we need to find views by IDs. - * - * This object also contains methods for inflating action views and binding action behaviors. This - * is a way of isolating view logic from the CallLogAdapter. We should consider moving that logic - * if the call log list item is eventually represented as a UI component. - */ -public final class CallLogListItemViews extends RecyclerView.ViewHolder { - /** The root view of the call log list item */ - public final View rootView; - /** The quick contact badge for the contact. */ - public final QuickContactBadge quickContactView; - /** The primary action view of the entry. */ - public final View primaryActionView; - /** The details of the phone call. */ - public final PhoneCallDetailsViews phoneCallDetailsViews; - /** The text of the header for a day grouping. */ - public final TextView dayGroupHeader; - /** The view containing the details for the call log row, including the action buttons. */ - public final CardView callLogEntryView; - /** The view containing call log item actions. Null until the ViewStub is inflated. */ - public View actionsView; - /** The "call back" action button - assigned only when the action section is expanded. */ - public TextView callBackButtonView; - /** The "video call" action button - assigned only when the action section is expanded. */ - public TextView videoCallButtonView; - /** The "voicemail" action button - assigned only when the action section is expanded. */ - public TextView voicemailButtonView; - /** The "details" action button - assigned only when the action section is expanded. */ - public TextView detailsButtonView; - /** The "report" action button. */ - public TextView reportButtonView; - - /** - * The row Id for the first call associated with the call log entry. Used as a key for the - * map used to track which call log entries have the action button section expanded. - */ - public long rowId; - - /** - * The call Ids for the calls represented by the current call log entry. Used when the user - * deletes a call log entry. - */ - public long[] callIds; - - /** - * The callable phone number for the current call log entry. Cached here as the call back - * intent is set only when the actions ViewStub is inflated. - */ - public String number; - - /** - * The phone number presentation for the current call log entry. Cached here as the call back - * intent is set only when the actions ViewStub is inflated. - */ - public int numberPresentation; - - /** - * The type of call for the current call log entry. Cached here as the call back - * intent is set only when the actions ViewStub is inflated. - */ - public int callType; - - /** - * The account for the current call log entry. Cached here as the call back - * intent is set only when the actions ViewStub is inflated. - */ - public PhoneAccountHandle accountHandle; - - /** - * If the call has an associated voicemail message, the URI of the voicemail message for - * playback. Cached here as the voicemail intent is only set when the actions ViewStub is - * inflated. - */ - public String voicemailUri; - - /** - * The name or number associated with the call. Cached here for use when setting content - * descriptions on buttons in the actions ViewStub when it is inflated. - */ - public CharSequence nameOrNumber; - - /** - * Whether or not the item has been reported by user as incorrect. - */ - public boolean reported; - - /** - * Whether or not the contact info can be marked as invalid from the source where - * it was obtained. - */ - public boolean canBeReportedAsInvalid; - - private static final int VOICEMAIL_TRANSCRIPTION_MAX_LINES = 10; - - private Context mContext; - private int mPhotoSize; - - - private CallLogListItemViews( - Context context, - View rootView, - QuickContactBadge quickContactView, - View primaryActionView, - PhoneCallDetailsViews phoneCallDetailsViews, - CardView callLogEntryView, - TextView dayGroupHeader) { - super(rootView); - mContext = context; - - this.rootView = rootView; - this.quickContactView = quickContactView; - this.primaryActionView = primaryActionView; - this.phoneCallDetailsViews = phoneCallDetailsViews; - this.callLogEntryView = callLogEntryView; - this.dayGroupHeader = dayGroupHeader; - - Resources resources = mContext.getResources(); - mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size); - } - - public static CallLogListItemViews fromView(Context context, View view) { - return new CallLogListItemViews( - context, - view, - (QuickContactBadge) view.findViewById(R.id.quick_contact_photo), - view.findViewById(R.id.primary_action_view), - PhoneCallDetailsViews.fromView(view), - (CardView) view.findViewById(R.id.call_log_row), - (TextView) view.findViewById(R.id.call_log_day_group_label)); - } - - /** - * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not - * inflated during initial binding, so click handlers, tags and accessibility text must be set - * here, if necessary. - * - * @param callLogItem The call log list item view. - */ - public void inflateActionViewStub( - final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener, - View.OnClickListener actionListener, - PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, - CallLogListItemHelper callLogViewsHelper) { - ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub); - if (stub != null) { - actionsView = (ViewGroup) stub.inflate(); - } - - if (callBackButtonView == null) { - callBackButtonView = (TextView) actionsView.findViewById(R.id.call_back_action); - } - - if (videoCallButtonView == null) { - videoCallButtonView = (TextView) actionsView.findViewById(R.id.video_call_action); - } - - if (voicemailButtonView == null) { - voicemailButtonView = (TextView) actionsView.findViewById(R.id.voicemail_action); - } - - if (detailsButtonView == null) { - detailsButtonView = (TextView) actionsView.findViewById(R.id.details_action); - } - - if (reportButtonView == null) { - reportButtonView = (TextView) actionsView.findViewById(R.id.report_action); - reportButtonView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (onReportButtonClickListener != null) { - onReportButtonClickListener.onReportButtonClick(number); - } - } - }); - } - - bindActionButtons(actionListener, phoneNumberUtilsWrapper, callLogViewsHelper); - } - - /** - * Binds text titles, click handlers and intents to the voicemail, details and callback action - * buttons. - */ - private void bindActionButtons( - View.OnClickListener actionListener, - PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, - CallLogListItemHelper callLogViewsHelper) { - boolean canPlaceCallToNumber = - PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation); - - // Set return call intent, otherwise null. - if (canPlaceCallToNumber) { - boolean isVoicemailNumber = - phoneNumberUtilsWrapper.isVoicemailNumber(accountHandle, number); - if (isVoicemailNumber) { - // Make a general call to voicemail to ensure that if there are multiple accounts - // it does not call the voicemail number of a specific phone account. - callBackButtonView.setTag(IntentProvider.getReturnVoicemailCallIntentProvider()); - } else { - // Sets the primary action to call the number. - callBackButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number)); - } - callBackButtonView.setVisibility(View.VISIBLE); - callBackButtonView.setOnClickListener(actionListener); - - final int titleId; - if (callType == Calls.VOICEMAIL_TYPE || callType == Calls.OUTGOING_TYPE) { - titleId = R.string.call_log_action_redial; - } else { - titleId = R.string.call_log_action_call_back; - } - callBackButtonView.setText(mContext.getString(titleId)); - } else { - // Number is not callable, so hide button. - callBackButtonView.setTag(null); - callBackButtonView.setVisibility(View.GONE); - } - - // If one of the calls had video capabilities, show the video call button. - if (CallUtil.isVideoEnabled(mContext) && canPlaceCallToNumber && - phoneCallDetailsViews.callTypeIcons.isVideoShown()) { - videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number)); - videoCallButtonView.setVisibility(View.VISIBLE); - videoCallButtonView.setOnClickListener(actionListener); - } else { - videoCallButtonView.setTag(null); - videoCallButtonView.setVisibility(View.GONE); - } - - // For voicemail calls, show the "VOICEMAIL" action button; hide otherwise. - if (callType == Calls.VOICEMAIL_TYPE) { - voicemailButtonView.setOnClickListener(actionListener); - voicemailButtonView.setTag( - IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri)); - voicemailButtonView.setVisibility(View.VISIBLE); - - detailsButtonView.setVisibility(View.GONE); - } else { - voicemailButtonView.setTag(null); - voicemailButtonView.setVisibility(View.GONE); - - detailsButtonView.setOnClickListener(actionListener); - detailsButtonView.setTag( - IntentProvider.getCallDetailIntentProvider(rowId, callIds, null)); - - if (canBeReportedAsInvalid && !reported) { - reportButtonView.setVisibility(View.VISIBLE); - } else { - reportButtonView.setVisibility(View.GONE); - } - } - - callLogViewsHelper.setActionContentDescriptions(this); - } - - /** - * Expands or collapses the view containing the CALLBACK/REDIAL, VOICEMAIL and DETAILS action - * buttons. - * - * TODO: Reduce number of classes which need to be passed in to inflate the action view stub. - * 1) Instantiate them in this class, and store local references. - * 2) Set them on the CallLogListItemHelper and use it for inflation. - * 3) Implement a parent view for a call log list item, and store references in that class. - */ - public void expandOrCollapseActions( - boolean isExpanded, - final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener, - View.OnClickListener actionListener, - PhoneNumberUtilsWrapper phoneNumberUtilsWrapper, - CallLogListItemHelper callLogViewsHelper) { - expandVoicemailTranscriptionView(isExpanded); - - if (isExpanded) { - // Inflate the view stub if necessary, and wire up the event handlers. - inflateActionViewStub(onReportButtonClickListener, actionListener, - phoneNumberUtilsWrapper, callLogViewsHelper); - - actionsView.setVisibility(View.VISIBLE); - actionsView.setAlpha(1.0f); - } else { - // When recycling a view, it is possible the actionsView ViewStub was previously - // inflated so we should hide it in this case. - if (actionsView != null) { - actionsView.setVisibility(View.GONE); - } - } - } - - public void expandVoicemailTranscriptionView(boolean isExpanded) { - if (callType != Calls.VOICEMAIL_TYPE) { - return; - } - - final TextView view = phoneCallDetailsViews.voicemailTranscriptionView; - if (TextUtils.isEmpty(view.getText())) { - return; - } - view.setMaxLines(isExpanded ? VOICEMAIL_TRANSCRIPTION_MAX_LINES : 1); - view.setSingleLine(!isExpanded); - } - - public void setPhoto(long photoId, Uri photoUri, Uri contactUri, String displayName, - boolean isVoicemail, boolean isBusiness) { - quickContactView.assignContactUri(contactUri); - quickContactView.setOverlay(null); - - int contactType = ContactPhotoManager.TYPE_DEFAULT; - if (isVoicemail) { - contactType = ContactPhotoManager.TYPE_VOICEMAIL; - } else if (isBusiness) { - contactType = ContactPhotoManager.TYPE_BUSINESS; - } - - String lookupKey = null; - if (contactUri != null) { - lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri); - } - - DefaultImageRequest request = new DefaultImageRequest( - displayName, lookupKey, contactType, true /* isCircular */); - - if (photoId == 0 && photoUri != null) { - ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, photoUri, - mPhotoSize, false /* darkTheme */, true /* isCircular */, request); - } else { - ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, photoId, - false /* darkTheme */, true /* isCircular */, request); - } - } - - @NeededForTesting - public static CallLogListItemViews createForTest(Context context) { - CallLogListItemViews views = new CallLogListItemViews( - context, - new View(context), - new QuickContactBadge(context), - new View(context), - PhoneCallDetailsViews.createForTest(context), - new CardView(context), - new TextView(context)); - views.callBackButtonView = new TextView(context); - views.voicemailButtonView = new TextView(context); - views.detailsButtonView = new TextView(context); - views.reportButtonView = new TextView(context); - views.actionsView = new View(context); - return views; - } -} -- cgit v1.2.3