1/*
2 * Copyright (C) 2010 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.contacts.quickcontact;
18
19import android.content.ComponentName;
20import android.content.ContentUris;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.graphics.drawable.Drawable;
25import android.net.Uri;
26import android.provider.ContactsContract.CommonDataKinds.Im;
27import android.provider.ContactsContract.Data;
28import android.telecom.PhoneAccount;
29import android.text.TextUtils;
30import android.util.Log;
31
32import com.android.contacts.R;
33import com.android.contacts.common.CallUtil;
34import com.android.contacts.common.ContactsUtils;
35import com.android.contacts.common.MoreContactUtils;
36import com.android.contacts.common.model.account.AccountType.EditType;
37import com.android.contacts.common.model.dataitem.DataItem;
38import com.android.contacts.common.model.dataitem.DataKind;
39import com.android.contacts.common.model.dataitem.EmailDataItem;
40import com.android.contacts.common.model.dataitem.ImDataItem;
41import com.android.contacts.common.model.dataitem.PhoneDataItem;
42import com.android.contacts.common.model.dataitem.SipAddressDataItem;
43import com.android.contacts.common.model.dataitem.StructuredPostalDataItem;
44import com.android.contacts.common.model.dataitem.WebsiteDataItem;
45import com.android.contacts.quickcontact.WebAddress.ParseException;
46import com.android.contacts.util.PhoneCapabilityTester;
47import com.android.contacts.util.StructuredPostalUtils;
48
49/**
50 * Description of a specific {@link Data#_ID} item, with style information
51 * defined by a {@link DataKind}.
52 */
53public class DataAction implements Action {
54    private static final String TAG = "DataAction";
55
56    private final Context mContext;
57    private final DataKind mKind;
58    private final String mMimeType;
59    private final Integer mTimesUsed;
60    private final Long mLastTimeUsed;
61
62    private CharSequence mBody;
63    private CharSequence mSubtitle;
64    private Intent mIntent;
65    private Intent mAlternateIntent;
66    private int mAlternateIconDescriptionRes;
67    private int mAlternateIconRes;
68    private int mPresence = -1;
69
70    private Uri mDataUri;
71    private long mDataId;
72    private boolean mIsPrimary;
73    private boolean mIsSuperPrimary;
74
75    /**
76     * Create an action from common {@link Data} elements.
77     */
78    public DataAction(Context context, DataItem item, DataKind kind) {
79        mContext = context;
80        mKind = kind;
81        mMimeType = item.getMimeType();
82        mTimesUsed = item.getTimesUsed();
83        mLastTimeUsed = item.getLastTimeUsed();
84
85        // Determine type for subtitle
86        mSubtitle = "";
87        if (item.hasKindTypeColumn(kind)) {
88            final int typeValue = item.getKindTypeColumn(kind);
89
90            // get type string
91            for (EditType type : kind.typeList) {
92                if (type.rawValue == typeValue) {
93                    if (type.customColumn == null) {
94                        // Non-custom type. Get its description from the resource
95                        mSubtitle = context.getString(type.labelRes);
96                    } else {
97                        // Custom type. Read it from the database
98                        mSubtitle = item.getContentValues().getAsString(type.customColumn);
99                    }
100                    break;
101                }
102            }
103        }
104
105        mIsPrimary = item.isPrimary();
106        mIsSuperPrimary = item.isSuperPrimary();
107        mBody = item.buildDataStringForDisplay(context, kind);
108
109        mDataId = item.getId();
110        mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId);
111
112        final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext);
113        final ComponentName smsComponent = PhoneCapabilityTester.getSmsComponent(mContext);
114        final boolean hasSms = (smsComponent != null);
115
116        // Handle well-known MIME-types with special care
117        if (item instanceof PhoneDataItem) {
118            if (PhoneCapabilityTester.isPhone(mContext)) {
119                PhoneDataItem phone = (PhoneDataItem) item;
120                final String number = phone.getNumber();
121                if (!TextUtils.isEmpty(number)) {
122
123                    final Intent phoneIntent = hasPhone ? CallUtil.getCallIntent(number)
124                            : null;
125                    Intent smsIntent = null;
126                    if (hasSms) {
127                        smsIntent = new Intent(Intent.ACTION_SENDTO,
128                                Uri.fromParts(ContactsUtils.SCHEME_SMSTO, number, null));
129                        smsIntent.setComponent(smsComponent);
130                    }
131
132                    // Configure Icons and Intents. Notice actionIcon is already set to the phone
133                    if (hasPhone && hasSms) {
134                        mIntent = phoneIntent;
135                        mAlternateIntent = smsIntent;
136                        mAlternateIconRes = kind.iconAltRes;
137                        mAlternateIconDescriptionRes = kind.iconAltDescriptionRes;
138                    } else if (hasPhone) {
139                        mIntent = phoneIntent;
140                    } else if (hasSms) {
141                        mIntent = smsIntent;
142                    }
143                }
144            }
145        } else if (item instanceof SipAddressDataItem) {
146            if (PhoneCapabilityTester.isSipPhone(mContext)) {
147                final SipAddressDataItem sip = (SipAddressDataItem) item;
148                final String address = sip.getSipAddress();
149                if (!TextUtils.isEmpty(address)) {
150                    final Uri callUri = Uri.fromParts(PhoneAccount.SCHEME_SIP, address, null);
151                    mIntent = CallUtil.getCallIntent(callUri);
152                    // Note that this item will get a SIP-specific variant
153                    // of the "call phone" icon, rather than the standard
154                    // app icon for the Phone app (which we show for
155                    // regular phone numbers.)  That's because the phone
156                    // app explicitly specifies an android:icon attribute
157                    // for the SIP-related intent-filters in its manifest.
158                }
159            }
160        } else if (item instanceof EmailDataItem) {
161            final EmailDataItem email = (EmailDataItem) item;
162            final String address = email.getData();
163            if (!TextUtils.isEmpty(address)) {
164                final Uri mailUri = Uri.fromParts(ContactsUtils.SCHEME_MAILTO, address, null);
165                mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
166            }
167
168        } else if (item instanceof WebsiteDataItem) {
169            final WebsiteDataItem website = (WebsiteDataItem) item;
170            final String url = website.getUrl();
171            if (!TextUtils.isEmpty(url)) {
172                try {
173                    final WebAddress webAddress = new WebAddress(url);
174                    mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
175                } catch (ParseException e) {
176                    mIntent = null;
177                }
178            }
179
180        } else if (item instanceof ImDataItem) {
181            ImDataItem im = (ImDataItem) item;
182            final boolean isEmail = im.isCreatedFromEmail();
183            if (isEmail || im.isProtocolValid()) {
184                final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol();
185
186                if (isEmail) {
187                    // Use Google Talk string when using Email, and clear data
188                    // Uri so we don't try saving Email as primary.
189                    mSubtitle = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK,
190                            null);
191                    mDataUri = null;
192                }
193
194                String host = im.getCustomProtocol();
195                String data = im.getData();
196                if (protocol != Im.PROTOCOL_CUSTOM) {
197                    // Try bringing in a well-known host for specific protocols
198                    host = ContactsUtils.lookupProviderNameFromId(protocol);
199                }
200
201                if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
202                    final String authority = host.toLowerCase();
203                    final Uri imUri = new Uri.Builder().scheme(ContactsUtils.SCHEME_IMTO).authority(
204                            authority).appendPath(data).build();
205                    mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
206
207                    // If the address is also available for a video chat, we'll show the capability
208                    // as a secondary action.
209                    final int chatCapability = im.getChatCapability();
210                    final boolean isVideoChatCapable =
211                            (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
212                    final boolean isAudioChatCapable =
213                            (chatCapability & Im.CAPABILITY_HAS_VOICE) != 0;
214                    if (isVideoChatCapable || isAudioChatCapable) {
215                        mAlternateIntent = new Intent(
216                                Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
217                        if (isVideoChatCapable) {
218                            mAlternateIconRes = R.drawable.sym_action_videochat_holo_light;
219                            mAlternateIconDescriptionRes = R.string.video_chat;
220                        } else {
221                            mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light;
222                            mAlternateIconDescriptionRes = R.string.audio_chat;
223                        }
224                    }
225                }
226            }
227        } else if (item instanceof StructuredPostalDataItem) {
228            StructuredPostalDataItem postal = (StructuredPostalDataItem) item;
229            final String postalAddress = postal.getFormattedAddress();
230            if (!TextUtils.isEmpty(postalAddress)) {
231                mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
232            }
233        }
234
235        if (mIntent == null) {
236            // Otherwise fall back to default VIEW action
237            mIntent = new Intent(Intent.ACTION_VIEW);
238            mIntent.setDataAndType(mDataUri, item.getMimeType());
239        }
240
241        mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
242    }
243
244    @Override
245    public int getPresence() {
246        return mPresence;
247    }
248
249    public void setPresence(int presence) {
250        mPresence = presence;
251    }
252
253    @Override
254    public CharSequence getSubtitle() {
255        return mSubtitle;
256    }
257
258    @Override
259    public CharSequence getBody() {
260        return mBody;
261    }
262
263    @Override
264    public String getMimeType() {
265        return mMimeType;
266    }
267
268    @Override
269    public Uri getDataUri() {
270        return mDataUri;
271    }
272
273    @Override
274    public long getDataId() {
275        return mDataId;
276    }
277
278    @Override
279    public boolean isPrimary() {
280        return mIsPrimary;
281    }
282
283    @Override
284    public boolean isSuperPrimary() {
285        return mIsSuperPrimary;
286    }
287
288    @Override
289    public Drawable getAlternateIcon() {
290        if (mAlternateIconRes == 0) return null;
291
292        final String resourcePackageName = mKind.resourcePackageName;
293        if (resourcePackageName == null) {
294            return mContext.getResources().getDrawable(mAlternateIconRes);
295        }
296
297        final PackageManager pm = mContext.getPackageManager();
298        return pm.getDrawable(resourcePackageName, mAlternateIconRes, null);
299    }
300
301    @Override
302    public String getAlternateIconDescription() {
303        if (mAlternateIconDescriptionRes == 0) return null;
304        return mContext.getResources().getString(mAlternateIconDescriptionRes);
305    }
306
307    @Override
308    public Intent getIntent() {
309        return mIntent;
310    }
311
312    @Override
313    public Intent getAlternateIntent() {
314        return mAlternateIntent;
315    }
316
317    @Override
318    public void collapseWith(Action other) {
319        // No-op
320    }
321
322    @Override
323    public boolean shouldCollapseWith(Action t, Context context) {
324        if (t == null) {
325            return false;
326        }
327        if (!(t instanceof DataAction)) {
328            Log.e(TAG, "t must be DataAction");
329            return false;
330        }
331        DataAction that = (DataAction)t;
332        if (!MoreContactUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) {
333            return false;
334        }
335        if (!TextUtils.equals(mMimeType, that.mMimeType)
336                || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) {
337            return false;
338        }
339        return true;
340    }
341
342    @Override
343    public Integer getTimesUsed() {
344        return mTimesUsed;
345    }
346
347    @Override
348    public Long getLastTimeUsed() {
349        return mLastTimeUsed;
350    }
351}
352