1 /*
2  * Copyright (C) 2017 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.calldetails;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.provider.CallLog.Calls;
22 import android.support.annotation.ColorInt;
23 import android.support.annotation.NonNull;
24 import android.support.v4.content.ContextCompat;
25 import android.support.v4.os.BuildCompat;
26 import android.support.v7.widget.RecyclerView.ViewHolder;
27 import android.text.TextUtils;
28 import android.view.View;
29 import android.widget.ImageView;
30 import android.widget.TextView;
31 import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
32 import com.android.dialer.calllogutils.CallLogDates;
33 import com.android.dialer.calllogutils.CallLogDurations;
34 import com.android.dialer.calllogutils.CallTypeHelper;
35 import com.android.dialer.calllogutils.CallTypeIconsView;
36 import com.android.dialer.common.LogUtil;
37 import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult;
38 import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult.Type;
39 import com.android.dialer.glidephotomanager.PhotoInfo;
40 import com.android.dialer.oem.MotorolaUtils;
41 import com.android.dialer.util.DialerUtils;
42 import com.android.dialer.util.IntentUtil;
43 
44 /** ViewHolder for call entries in {@link OldCallDetailsActivity} or {@link CallDetailsActivity}. */
45 public class CallDetailsEntryViewHolder extends ViewHolder {
46 
47   /** Listener for the call details header */
48   interface CallDetailsEntryListener {
49     /** Shows RTT transcript. */
showRttTranscript(String transcriptId, String primaryText, PhotoInfo photoInfo)50     void showRttTranscript(String transcriptId, String primaryText, PhotoInfo photoInfo);
51   }
52 
53   private final CallDetailsEntryListener callDetailsEntryListener;
54 
55   private final CallTypeIconsView callTypeIcon;
56   private final TextView callTypeText;
57   private final TextView callTime;
58   private final TextView callDuration;
59 
60   private final View multimediaImageContainer;
61   private final View multimediaDetailsContainer;
62   private final View multimediaDivider;
63 
64   private final TextView multimediaDetails;
65   private final TextView postCallNote;
66   private final TextView rttTranscript;
67 
68   private final ImageView multimediaImage;
69 
70   // TODO(maxwelb): Display this when location is stored - a bug
71   @SuppressWarnings("unused")
72   private final TextView multimediaAttachmentsNumber;
73 
74   private final Context context;
75 
CallDetailsEntryViewHolder( View container, CallDetailsEntryListener callDetailsEntryListener)76   public CallDetailsEntryViewHolder(
77       View container, CallDetailsEntryListener callDetailsEntryListener) {
78     super(container);
79     context = container.getContext();
80 
81     callTypeIcon = (CallTypeIconsView) container.findViewById(R.id.call_direction);
82     callTypeText = (TextView) container.findViewById(R.id.call_type);
83     callTime = (TextView) container.findViewById(R.id.call_time);
84     callDuration = (TextView) container.findViewById(R.id.call_duration);
85 
86     multimediaImageContainer = container.findViewById(R.id.multimedia_image_container);
87     multimediaDetailsContainer = container.findViewById(R.id.ec_container);
88     multimediaDivider = container.findViewById(R.id.divider);
89     multimediaDetails = (TextView) container.findViewById(R.id.multimedia_details);
90     postCallNote = (TextView) container.findViewById(R.id.post_call_note);
91     multimediaImage = (ImageView) container.findViewById(R.id.multimedia_image);
92     multimediaAttachmentsNumber =
93         (TextView) container.findViewById(R.id.multimedia_attachments_number);
94     rttTranscript = container.findViewById(R.id.rtt_transcript);
95     this.callDetailsEntryListener = callDetailsEntryListener;
96   }
97 
setCallDetails( String number, String primaryText, PhotoInfo photoInfo, CallDetailsEntry entry, CallTypeHelper callTypeHelper, boolean showMultimediaDivider)98   void setCallDetails(
99       String number,
100       String primaryText,
101       PhotoInfo photoInfo,
102       CallDetailsEntry entry,
103       CallTypeHelper callTypeHelper,
104       boolean showMultimediaDivider) {
105     int callType = entry.getCallType();
106     boolean isVideoCall = (entry.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO;
107     boolean isPulledCall =
108         (entry.getFeatures() & Calls.FEATURES_PULLED_EXTERNALLY)
109             == Calls.FEATURES_PULLED_EXTERNALLY;
110     boolean isDuoCall = entry.getIsDuoCall();
111     boolean isRttCall =
112         BuildCompat.isAtLeastP()
113             && (entry.getFeatures() & Calls.FEATURES_RTT) == Calls.FEATURES_RTT;
114 
115     callTime.setTextColor(getColorForCallType(context, callType));
116     callTypeIcon.clear();
117     callTypeIcon.add(callType);
118     callTypeIcon.setShowVideo(isVideoCall);
119     callTypeIcon.setShowHd(
120         (entry.getFeatures() & Calls.FEATURES_HD_CALL) == Calls.FEATURES_HD_CALL);
121     callTypeIcon.setShowWifi(
122         MotorolaUtils.shouldShowWifiIconInCallLog(context, entry.getFeatures()));
123     if (BuildCompat.isAtLeastP()) {
124       callTypeIcon.setShowRtt((entry.getFeatures() & Calls.FEATURES_RTT) == Calls.FEATURES_RTT);
125     }
126 
127     callTypeText.setText(
128         callTypeHelper.getCallTypeText(callType, isVideoCall, isPulledCall, isDuoCall));
129     callTime.setText(CallLogDates.formatDate(context, entry.getDate()));
130 
131     if (CallTypeHelper.isMissedCallType(callType)) {
132       callDuration.setVisibility(View.GONE);
133     } else {
134       callDuration.setVisibility(View.VISIBLE);
135       callDuration.setText(
136           CallLogDurations.formatDurationAndDataUsage(
137               context, entry.getDuration(), entry.getDataUsage()));
138       callDuration.setContentDescription(
139           CallLogDurations.formatDurationAndDataUsageA11y(
140               context, entry.getDuration(), entry.getDataUsage()));
141     }
142     setMultimediaDetails(number, entry, showMultimediaDivider);
143     if (isRttCall) {
144       if (entry.getHasRttTranscript()) {
145         rttTranscript.setText(R.string.rtt_transcript_link);
146         rttTranscript.setTextAppearance(R.style.RttTranscriptLink);
147         rttTranscript.setClickable(true);
148         rttTranscript.setOnClickListener(
149             v ->
150                 callDetailsEntryListener.showRttTranscript(
151                     entry.getCallMappingId(), primaryText, photoInfo));
152       } else {
153         rttTranscript.setText(R.string.rtt_transcript_not_available);
154         rttTranscript.setTextAppearance(R.style.RttTranscriptMessage);
155         rttTranscript.setClickable(false);
156       }
157       rttTranscript.setVisibility(View.VISIBLE);
158     } else {
159       rttTranscript.setVisibility(View.GONE);
160     }
161   }
162 
setMultimediaDetails(String number, CallDetailsEntry entry, boolean showDivider)163   private void setMultimediaDetails(String number, CallDetailsEntry entry, boolean showDivider) {
164     multimediaDivider.setVisibility(showDivider ? View.VISIBLE : View.GONE);
165     if (entry.getHistoryResultsList().isEmpty()) {
166       LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no data, hiding UI");
167       multimediaDetailsContainer.setVisibility(View.GONE);
168     } else {
169 
170       HistoryResult historyResult = entry.getHistoryResults(0);
171       multimediaDetailsContainer.setVisibility(View.VISIBLE);
172       multimediaDetailsContainer.setOnClickListener((v) -> startSmsIntent(context, number));
173       multimediaImageContainer.setOnClickListener((v) -> startSmsIntent(context, number));
174       multimediaImageContainer.setClipToOutline(true);
175 
176       if (!TextUtils.isEmpty(historyResult.getImageUri())) {
177         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "setting image");
178         multimediaImageContainer.setVisibility(View.VISIBLE);
179         multimediaImage.setImageURI(Uri.parse(historyResult.getImageUri()));
180         multimediaDetails.setText(
181             isIncoming(historyResult) ? R.string.received_a_photo : R.string.sent_a_photo);
182       } else {
183         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no image");
184       }
185 
186       // Set text after image to overwrite the received/sent a photo text
187       if (!TextUtils.isEmpty(historyResult.getText())) {
188         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "showing text");
189         multimediaDetails.setText(
190             context.getString(R.string.message_in_quotes, historyResult.getText()));
191       } else {
192         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no text");
193       }
194 
195       if (entry.getHistoryResultsList().size() > 1
196           && !TextUtils.isEmpty(entry.getHistoryResults(1).getText())) {
197         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "showing post call note");
198         postCallNote.setVisibility(View.VISIBLE);
199         postCallNote.setText(
200             context.getString(R.string.message_in_quotes, entry.getHistoryResults(1).getText()));
201         postCallNote.setOnClickListener((v) -> startSmsIntent(context, number));
202       } else {
203         LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no post call note");
204       }
205     }
206   }
207 
startSmsIntent(Context context, String number)208   private void startSmsIntent(Context context, String number) {
209     DialerUtils.startActivityWithErrorToast(context, IntentUtil.getSendSmsIntent(number));
210   }
211 
isIncoming(@onNull HistoryResult historyResult)212   private static boolean isIncoming(@NonNull HistoryResult historyResult) {
213     return historyResult.getType() == Type.INCOMING_POST_CALL
214         || historyResult.getType() == Type.INCOMING_CALL_COMPOSER;
215   }
216 
getColorForCallType(Context context, int callType)217   private static @ColorInt int getColorForCallType(Context context, int callType) {
218     switch (callType) {
219       case Calls.OUTGOING_TYPE:
220       case Calls.VOICEMAIL_TYPE:
221       case Calls.BLOCKED_TYPE:
222       case Calls.INCOMING_TYPE:
223       case Calls.ANSWERED_EXTERNALLY_TYPE:
224       case Calls.REJECTED_TYPE:
225         return ContextCompat.getColor(context, R.color.dialer_secondary_text_color);
226       case Calls.MISSED_TYPE:
227       default:
228         // It is possible for users to end up with calls with unknown call types in their
229         // call history, possibly due to 3rd party call log implementations (e.g. to
230         // distinguish between rejected and missed calls). Instead of crashing, just
231         // assume that all unknown call types are missed calls.
232         return ContextCompat.getColor(context, R.color.dialer_red);
233     }
234   }
235 }
236