1/*
2 * Copyright (C) 2011 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
17package com.android.dialer.calllog;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.provider.CallLog.Calls;
22import android.text.SpannableStringBuilder;
23import android.text.TextUtils;
24import android.util.Log;
25
26import com.android.dialer.PhoneCallDetails;
27import com.android.dialer.R;
28
29/**
30 * Helper class to fill in the views of a call log entry.
31 */
32/* package */class CallLogListItemHelper {
33    private static final String TAG = "CallLogListItemHelper";
34
35    /** Helper for populating the details of a phone call. */
36    private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
37    /** Resources to look up strings. */
38    private final Resources mResources;
39    private final TelecomCallLogCache mTelecomCallLogCache;
40
41    /**
42     * Creates a new helper instance.
43     *
44     * @param phoneCallDetailsHelper used to set the details of a phone call
45     * @param phoneNumberHelper used to process phone number
46     */
47    public CallLogListItemHelper(
48            PhoneCallDetailsHelper phoneCallDetailsHelper,
49            Resources resources,
50            TelecomCallLogCache telecomCallLogCache) {
51        mPhoneCallDetailsHelper = phoneCallDetailsHelper;
52        mResources = resources;
53        mTelecomCallLogCache = telecomCallLogCache;
54    }
55
56    /**
57     * Sets the name, label, and number for a contact.
58     *
59     * @param context The application context.
60     * @param views the views to populate
61     * @param details the details of a phone call needed to fill in the data
62     */
63    public void setPhoneCallDetails(
64            CallLogListItemViewHolder views,
65            PhoneCallDetails details) {
66        mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
67
68        // Set the accessibility text for the contact badge
69        views.quickContactView.setContentDescription(getContactBadgeDescription(details));
70
71        // Set the primary action accessibility description
72        views.primaryActionView.setContentDescription(getCallDescription(details));
73
74        // Cache name or number of caller.  Used when setting the content descriptions of buttons
75        // when the actions ViewStub is inflated.
76        views.nameOrNumber = getNameOrNumber(details);
77    }
78
79    /**
80     * Sets the accessibility descriptions for the action buttons in the action button ViewStub.
81     *
82     * @param views The views associated with the current call log entry.
83     */
84    public void setActionContentDescriptions(CallLogListItemViewHolder views) {
85        if (views.nameOrNumber == null) {
86            Log.e(TAG, "setActionContentDescriptions; name or number is null.");
87        }
88
89        // Calling expandTemplate with a null parameter will cause a NullPointerException.
90        // Although we don't expect a null name or number, it is best to protect against it.
91        CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber;
92
93        views.videoCallButtonView.setContentDescription(
94                TextUtils.expandTemplate(
95                        mResources.getString(R.string.description_video_call_action),
96                        nameOrNumber));
97
98        views.createNewContactButtonView.setContentDescription(
99                TextUtils.expandTemplate(
100                        mResources.getString(R.string.description_create_new_contact_action),
101                        nameOrNumber));
102
103        views.addToExistingContactButtonView.setContentDescription(
104                TextUtils.expandTemplate(
105                        mResources.getString(R.string.description_add_to_existing_contact_action),
106                        nameOrNumber));
107
108        views.detailsButtonView.setContentDescription(
109                TextUtils.expandTemplate(
110                        mResources.getString(R.string.description_details_action), nameOrNumber));
111    }
112
113    /**
114     * Returns the accessibility description for the contact badge for a call log entry.
115     *
116     * @param details Details of call.
117     * @return Accessibility description.
118     */
119    private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
120        return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
121    }
122
123    /**
124     * Returns the accessibility description of the "return call/call" action for a call log
125     * entry.
126     * Accessibility text is a combination of:
127     * {Voicemail Prefix}. {Number of Calls}. {Caller information} {Phone Account}.
128     * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
129     *
130     * If more than one call for the caller, {Number of Calls} is:
131     * "{number of calls} calls.", otherwise "".
132     *
133     * The {Caller Information} references the most recent call associated with the caller.
134     * For incoming calls:
135     * If missed call:  Missed call from {Name/Number} {Call Type} {Call Time}.
136     * If answered call: Answered call from {Name/Number} {Call Type} {Call Time}.
137     *
138     * For outgoing calls:
139     * If outgoing:  Call to {Name/Number] {Call Type} {Call Time}.
140     *
141     * Where:
142     * {Name/Number} is the name or number of the caller (as shown in call log).
143     * {Call type} is the contact phone number type (eg mobile) or location.
144     * {Call Time} is the time since the last call for the contact occurred.
145     *
146     * The {Phone Account} refers to the account/SIM through which the call was placed or received
147     * in multi-SIM devices.
148     *
149     * Examples:
150     * 3 calls.  New Voicemail.  Missed call from Joe Smith mobile 2 hours ago on SIM 1.
151     *
152     * 2 calls.  Answered call from John Doe mobile 1 hour ago.
153     *
154     * @param context The application context.
155     * @param details Details of call.
156     * @return Return call action description.
157     */
158    public CharSequence getCallDescription(PhoneCallDetails details) {
159        int lastCallType = getLastCallType(details.callTypes);
160        boolean isVoiceMail = lastCallType == Calls.VOICEMAIL_TYPE;
161
162        // Get the name or number of the caller.
163        final CharSequence nameOrNumber = getNameOrNumber(details);
164
165        // Get the call type or location of the caller; null if not applicable
166        final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
167
168        // Get the time/date of the call
169        final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
170
171        SpannableStringBuilder callDescription = new SpannableStringBuilder();
172
173        // Prepend the voicemail indication.
174        if (isVoiceMail) {
175            callDescription.append(mResources.getString(R.string.description_new_voicemail));
176        }
177
178        // Add number of calls if more than one.
179        if (details.callTypes.length > 1) {
180            callDescription.append(mResources.getString(R.string.description_num_calls,
181                    details.callTypes.length));
182        }
183
184        // If call had video capabilities, add the "Video Call" string.
185        if ((details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
186            callDescription.append(mResources.getString(R.string.description_video_call));
187        }
188
189        int stringID = getCallDescriptionStringID(details.callTypes);
190        String accountLabel = mTelecomCallLogCache.getAccountLabel(details.accountHandle);
191
192        // Use chosen string resource to build up the message.
193        CharSequence onAccountLabel = accountLabel == null
194                ? ""
195                : TextUtils.expandTemplate(
196                        mResources.getString(R.string.description_phone_account),
197                        accountLabel);
198        callDescription.append(
199                TextUtils.expandTemplate(
200                        mResources.getString(stringID),
201                        nameOrNumber,
202                        // If no type or location can be determined, sub in empty string.
203                        typeOrLocation == null ? "" : typeOrLocation,
204                        timeOfCall,
205                        onAccountLabel));
206
207        return callDescription;
208    }
209
210    /**
211     * Determine the appropriate string ID to describe a call for accessibility purposes.
212     *
213     * @param details Call details.
214     * @return String resource ID to use.
215     */
216    public int getCallDescriptionStringID(int[] callTypes) {
217        int lastCallType = getLastCallType(callTypes);
218        int stringID;
219
220        if (lastCallType == Calls.VOICEMAIL_TYPE || lastCallType == Calls.MISSED_TYPE) {
221            //Message: Missed call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
222            //<PhoneAccount>.
223            stringID = R.string.description_incoming_missed_call;
224        } else if (lastCallType == Calls.INCOMING_TYPE) {
225            //Message: Answered call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>,
226            //<PhoneAccount>.
227            stringID = R.string.description_incoming_answered_call;
228        } else {
229            //Message: Call to <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, <PhoneAccount>.
230            stringID = R.string.description_outgoing_call;
231        }
232        return stringID;
233    }
234
235    /**
236     * Determine the call type for the most recent call.
237     * @param callTypes Call types to check.
238     * @return Call type.
239     */
240    private int getLastCallType(int[] callTypes) {
241        if (callTypes.length > 0) {
242            return callTypes[0];
243        } else {
244            return Calls.MISSED_TYPE;
245        }
246    }
247
248    /**
249     * Return the name or number of the caller specified by the details.
250     * @param details Call details
251     * @return the name (if known) of the caller, otherwise the formatted number.
252     */
253    private CharSequence getNameOrNumber(PhoneCallDetails details) {
254        final CharSequence recipient;
255        if (!TextUtils.isEmpty(details.name)) {
256            recipient = details.name;
257        } else {
258            recipient = details.displayNumber;
259        }
260        return recipient;
261    }
262}
263