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.net.WebAddress;
27import android.provider.ContactsContract.CommonDataKinds.Im;
28import android.provider.ContactsContract.Data;
29import android.telecom.PhoneAccount;
30import android.text.TextUtils;
31import android.util.Log;
32
33import com.android.contacts.R;
34import com.android.contacts.common.CallUtil;
35import com.android.contacts.common.ContactsUtils;
36import com.android.contacts.common.MoreContactUtils;
37import com.android.contacts.common.model.account.AccountType.EditType;
38import com.android.contacts.common.model.dataitem.DataItem;
39import com.android.contacts.common.model.dataitem.DataKind;
40import com.android.contacts.common.model.dataitem.EmailDataItem;
41import com.android.contacts.common.model.dataitem.ImDataItem;
42import com.android.contacts.common.model.dataitem.PhoneDataItem;
43import com.android.contacts.common.model.dataitem.SipAddressDataItem;
44import com.android.contacts.common.model.dataitem.StructuredPostalDataItem;
45import com.android.contacts.common.model.dataitem.WebsiteDataItem;
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                WebAddress webAddress = new WebAddress(url);
173                mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
174            }
175
176        } else if (item instanceof ImDataItem) {
177            ImDataItem im = (ImDataItem) item;
178            final boolean isEmail = im.isCreatedFromEmail();
179            if (isEmail || im.isProtocolValid()) {
180                final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol();
181
182                if (isEmail) {
183                    // Use Google Talk string when using Email, and clear data
184                    // Uri so we don't try saving Email as primary.
185                    mSubtitle = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK,
186                            null);
187                    mDataUri = null;
188                }
189
190                String host = im.getCustomProtocol();
191                String data = im.getData();
192                if (protocol != Im.PROTOCOL_CUSTOM) {
193                    // Try bringing in a well-known host for specific protocols
194                    host = ContactsUtils.lookupProviderNameFromId(protocol);
195                }
196
197                if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
198                    final String authority = host.toLowerCase();
199                    final Uri imUri = new Uri.Builder().scheme(ContactsUtils.SCHEME_IMTO).authority(
200                            authority).appendPath(data).build();
201                    mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
202
203                    // If the address is also available for a video chat, we'll show the capability
204                    // as a secondary action.
205                    final int chatCapability = im.getChatCapability();
206                    final boolean isVideoChatCapable =
207                            (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
208                    final boolean isAudioChatCapable =
209                            (chatCapability & Im.CAPABILITY_HAS_VOICE) != 0;
210                    if (isVideoChatCapable || isAudioChatCapable) {
211                        mAlternateIntent = new Intent(
212                                Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
213                        if (isVideoChatCapable) {
214                            mAlternateIconRes = R.drawable.sym_action_videochat_holo_light;
215                            mAlternateIconDescriptionRes = R.string.video_chat;
216                        } else {
217                            mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light;
218                            mAlternateIconDescriptionRes = R.string.audio_chat;
219                        }
220                    }
221                }
222            }
223        } else if (item instanceof StructuredPostalDataItem) {
224            StructuredPostalDataItem postal = (StructuredPostalDataItem) item;
225            final String postalAddress = postal.getFormattedAddress();
226            if (!TextUtils.isEmpty(postalAddress)) {
227                mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
228            }
229        }
230
231        if (mIntent == null) {
232            // Otherwise fall back to default VIEW action
233            mIntent = new Intent(Intent.ACTION_VIEW);
234            mIntent.setDataAndType(mDataUri, item.getMimeType());
235        }
236
237        mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
238    }
239
240    @Override
241    public int getPresence() {
242        return mPresence;
243    }
244
245    public void setPresence(int presence) {
246        mPresence = presence;
247    }
248
249    @Override
250    public CharSequence getSubtitle() {
251        return mSubtitle;
252    }
253
254    @Override
255    public CharSequence getBody() {
256        return mBody;
257    }
258
259    @Override
260    public String getMimeType() {
261        return mMimeType;
262    }
263
264    @Override
265    public Uri getDataUri() {
266        return mDataUri;
267    }
268
269    @Override
270    public long getDataId() {
271        return mDataId;
272    }
273
274    @Override
275    public boolean isPrimary() {
276        return mIsPrimary;
277    }
278
279    @Override
280    public boolean isSuperPrimary() {
281        return mIsSuperPrimary;
282    }
283
284    @Override
285    public Drawable getAlternateIcon() {
286        if (mAlternateIconRes == 0) return null;
287
288        final String resourcePackageName = mKind.resourcePackageName;
289        if (resourcePackageName == null) {
290            return mContext.getResources().getDrawable(mAlternateIconRes);
291        }
292
293        final PackageManager pm = mContext.getPackageManager();
294        return pm.getDrawable(resourcePackageName, mAlternateIconRes, null);
295    }
296
297    @Override
298    public String getAlternateIconDescription() {
299        if (mAlternateIconDescriptionRes == 0) return null;
300        return mContext.getResources().getString(mAlternateIconDescriptionRes);
301    }
302
303    @Override
304    public Intent getIntent() {
305        return mIntent;
306    }
307
308    @Override
309    public Intent getAlternateIntent() {
310        return mAlternateIntent;
311    }
312
313    @Override
314    public void collapseWith(Action other) {
315        // No-op
316    }
317
318    @Override
319    public boolean shouldCollapseWith(Action t, Context context) {
320        if (t == null) {
321            return false;
322        }
323        if (!(t instanceof DataAction)) {
324            Log.e(TAG, "t must be DataAction");
325            return false;
326        }
327        DataAction that = (DataAction)t;
328        if (!MoreContactUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) {
329            return false;
330        }
331        if (!TextUtils.equals(mMimeType, that.mMimeType)
332                || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) {
333            return false;
334        }
335        return true;
336    }
337
338    @Override
339    public Integer getTimesUsed() {
340        return mTimesUsed;
341    }
342
343    @Override
344    public Long getLastTimeUsed() {
345        return mLastTimeUsed;
346    }
347}
348