blob: 415fa181d655dff131f82b1a00cff2161c95efd2 [file] [log] [blame]
Daniel Lehmannfbb87632011-09-30 20:54:15 -07001/*
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
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080017package com.android.contacts.quickcontact;
18
19import com.android.contacts.ContactsUtils;
20import com.android.contacts.R;
Daniel Lehmannedb576a2011-07-27 16:45:13 -070021import com.android.contacts.model.AccountType.EditType;
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -070022import com.android.contacts.model.DataKind;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080023import com.android.contacts.util.Constants;
Makoto Onukibfb59d82011-09-30 16:34:38 -070024import com.android.contacts.util.StructuredPostalUtils;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080025import com.android.contacts.util.PhoneCapabilityTester;
26
27import android.content.ContentUris;
28import android.content.Context;
29import android.content.Intent;
30import android.content.pm.PackageManager;
31import android.database.Cursor;
32import android.graphics.drawable.Drawable;
33import android.net.Uri;
Dmitri Plotnikov15dcf1d2010-12-09 17:12:54 -080034import android.net.WebAddress;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080035import android.provider.ContactsContract.CommonDataKinds.Email;
36import android.provider.ContactsContract.CommonDataKinds.Im;
37import android.provider.ContactsContract.CommonDataKinds.Phone;
38import android.provider.ContactsContract.CommonDataKinds.SipAddress;
Makoto Onukibfb59d82011-09-30 16:34:38 -070039import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080040import android.provider.ContactsContract.CommonDataKinds.Website;
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -070041import android.provider.ContactsContract.Data;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080042import android.text.TextUtils;
43import android.util.Log;
44
45/**
46 * Description of a specific {@link Data#_ID} item, with style information
47 * defined by a {@link DataKind}.
48 */
49public class DataAction implements Action {
50 private static final String TAG = "DataAction";
51
52 private final Context mContext;
53 private final DataKind mKind;
54 private final String mMimeType;
55
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080056 private CharSequence mBody;
Daniel Lehmannedb576a2011-07-27 16:45:13 -070057 private CharSequence mSubtitle;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080058 private Intent mIntent;
Daniel Lehmannedb576a2011-07-27 16:45:13 -070059 private Intent mAlternateIntent;
Katherine Kuanee05dcd2011-10-05 20:18:12 -070060 private int mAlternateIconDescriptionRes;
Daniel Lehmannedb576a2011-07-27 16:45:13 -070061 private int mAlternateIconRes;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080062
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080063 private Uri mDataUri;
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -080064 private long mDataId;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080065 private boolean mIsPrimary;
66
67 /**
68 * Create an action from common {@link Data} elements.
69 */
Daniel Lehmannedb576a2011-07-27 16:45:13 -070070 public DataAction(Context context, String mimeType, DataKind kind, long dataId, Cursor cursor) {
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080071 mContext = context;
72 mKind = kind;
73 mMimeType = mimeType;
74
Daniel Lehmannedb576a2011-07-27 16:45:13 -070075 // Determine type for subtitle
76 mSubtitle = "";
77 if (kind.typeColumn != null) {
78 final int typeColumnIndex = cursor.getColumnIndex(kind.typeColumn);
79 if (typeColumnIndex != -1) {
80 final int typeValue = cursor.getInt(typeColumnIndex);
81
82 // get type string
83 for (EditType type : kind.typeList) {
84 if (type.rawValue == typeValue) {
85 if (type.customColumn == null) {
86 // Non-custom type. Get its description from the resource
87 mSubtitle = context.getString(type.labelRes);
88 } else {
89 // Custom type. Read it from the database
90 mSubtitle = cursor.getString(cursor.getColumnIndexOrThrow(
91 type.customColumn));
92 }
93 break;
94 }
95 }
96 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -080097 }
98
99 if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
100 mIsPrimary = true;
101 }
102
103 if (mKind.actionBody != null) {
104 mBody = mKind.actionBody.inflateUsing(context, cursor);
105 }
106
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -0800107 mDataId = dataId;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800108 mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
109
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700110 final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext);
111 final boolean hasSms = PhoneCapabilityTester.isSmsIntentRegistered(mContext);
112
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800113 // Handle well-known MIME-types with special care
114 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
115 if (PhoneCapabilityTester.isPhone(mContext)) {
116 final String number = getAsString(cursor, Phone.NUMBER);
117 if (!TextUtils.isEmpty(number)) {
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700118
Daisuke Miyakawafadd5e12011-12-01 17:34:18 -0800119 final Intent phoneIntent = hasPhone ? ContactsUtils.getCallIntent(number)
120 : null;
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700121 final Intent smsIntent = hasSms ? new Intent(Intent.ACTION_SENDTO,
122 Uri.fromParts(Constants.SCHEME_SMSTO, number, null)) : null;
123
124 // Configure Icons and Intents. Notice actionIcon is already set to the phone
125 if (hasPhone && hasSms) {
126 mIntent = phoneIntent;
127 mAlternateIntent = smsIntent;
Daniel Lehmann1792b082011-08-23 18:48:22 -0700128 mAlternateIconRes = kind.iconAltRes;
Katherine Kuanee05dcd2011-10-05 20:18:12 -0700129 mAlternateIconDescriptionRes = kind.iconAltDescriptionRes;
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700130 } else if (hasPhone) {
131 mIntent = phoneIntent;
132 } else if (hasSms) {
133 mIntent = smsIntent;
134 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800135 }
136 }
137 } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
138 if (PhoneCapabilityTester.isSipPhone(mContext)) {
139 final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
140 if (!TextUtils.isEmpty(address)) {
141 final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
Daisuke Miyakawafadd5e12011-12-01 17:34:18 -0800142 mIntent = ContactsUtils.getCallIntent(callUri);
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800143 // Note that this item will get a SIP-specific variant
144 // of the "call phone" icon, rather than the standard
145 // app icon for the Phone app (which we show for
146 // regular phone numbers.) That's because the phone
147 // app explicitly specifies an android:icon attribute
148 // for the SIP-related intent-filters in its manifest.
149 }
150 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800151 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
152 final String address = getAsString(cursor, Email.DATA);
153 if (!TextUtils.isEmpty(address)) {
154 final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
155 mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
156 }
157
158 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
159 final String url = getAsString(cursor, Website.URL);
160 if (!TextUtils.isEmpty(url)) {
Dmitri Plotnikov15dcf1d2010-12-09 17:12:54 -0800161 WebAddress webAddress = new WebAddress(url);
162 mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800163 }
164
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700165 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800166 final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
167 getAsString(cursor, Data.MIMETYPE));
168 if (isEmail || isProtocolValid(cursor)) {
169 final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700170 getAsInt(cursor, Im.PROTOCOL);
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800171
172 if (isEmail) {
173 // Use Google Talk string when using Email, and clear data
174 // Uri so we don't try saving Email as primary.
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700175 mSubtitle = context.getText(R.string.chat_gtalk);
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800176 mDataUri = null;
177 }
178
179 String host = getAsString(cursor, Im.CUSTOM_PROTOCOL);
180 String data = getAsString(cursor,
181 isEmail ? Email.DATA : Im.DATA);
182 if (protocol != Im.PROTOCOL_CUSTOM) {
183 // Try bringing in a well-known host for specific protocols
184 host = ContactsUtils.lookupProviderNameFromId(protocol);
185 }
186
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700187 if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800188 final String authority = host.toLowerCase();
189 final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
190 authority).appendPath(data).build();
191 mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700192
193 // If the address is also available for a video chat, we'll show the capability
194 // as a secondary action.
195 final int chatCapability = getAsInt(cursor, Data.CHAT_CAPABILITY);
196 final boolean isVideoChatCapable =
197 (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
198 final boolean isAudioChatCapable =
199 (chatCapability & Im.CAPABILITY_HAS_VOICE) != 0;
200 if (isVideoChatCapable || isAudioChatCapable) {
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700201 mAlternateIntent = new Intent(
202 Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
Katherine Kuanee05dcd2011-10-05 20:18:12 -0700203 if (isVideoChatCapable) {
204 mAlternateIconRes = R.drawable.sym_action_videochat_holo_light;
205 mAlternateIconDescriptionRes = R.string.video_chat;
206 } else {
207 mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light;
208 mAlternateIconDescriptionRes = R.string.audio_chat;
209 }
Daisuke Miyakawaf31df5d2011-08-02 15:37:50 -0700210 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800211 }
212 }
Makoto Onukibfb59d82011-09-30 16:34:38 -0700213 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
214 final String postalAddress = getAsString(cursor, StructuredPostal.FORMATTED_ADDRESS);
215 if (!TextUtils.isEmpty(postalAddress)) {
216 mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
217 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800218 }
219
220 if (mIntent == null) {
221 // Otherwise fall back to default VIEW action
Dmitri Plotnikov1b89adc2010-12-09 17:05:54 -0800222 mIntent = new Intent(Intent.ACTION_VIEW);
223 mIntent.setDataAndType(mDataUri, mimeType);
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800224 }
225
226 // Always launch as new task, since we're like a launcher
227 mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
228 }
229
230 /** Read {@link String} from the given {@link Cursor}. */
231 private static String getAsString(Cursor cursor, String columnName) {
232 final int index = cursor.getColumnIndex(columnName);
233 return cursor.getString(index);
234 }
235
236 /** Read {@link Integer} from the given {@link Cursor}. */
237 private static int getAsInt(Cursor cursor, String columnName) {
238 final int index = cursor.getColumnIndex(columnName);
239 return cursor.getInt(index);
240 }
241
242 private boolean isProtocolValid(Cursor cursor) {
243 final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL);
244 if (cursor.isNull(columnIndex)) {
245 return false;
246 }
247 try {
248 Integer.valueOf(cursor.getString(columnIndex));
249 } catch (NumberFormatException e) {
250 return false;
251 }
252 return true;
253 }
254
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800255 @Override
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700256 public CharSequence getSubtitle() {
257 return mSubtitle;
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800258 }
259
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800260 @Override
261 public CharSequence getBody() {
262 return mBody;
263 }
264
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800265 @Override
266 public String getMimeType() {
267 return mMimeType;
268 }
269
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800270 @Override
271 public Uri getDataUri() {
272 return mDataUri;
273 }
274
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800275 @Override
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -0800276 public long getDataId() {
277 return mDataId;
278 }
279
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -0800280 @Override
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800281 public Boolean isPrimary() {
282 return mIsPrimary;
283 }
284
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800285 @Override
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700286 public Drawable getAlternateIcon() {
287 if (mAlternateIconRes == 0) return null;
288
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800289 final String resPackageName = mKind.resPackageName;
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700290 if (resPackageName == null) {
291 return mContext.getResources().getDrawable(mAlternateIconRes);
292 }
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800293
294 final PackageManager pm = mContext.getPackageManager();
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700295 return pm.getDrawable(resPackageName, mAlternateIconRes, null);
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800296 }
297
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800298 @Override
Katherine Kuanee05dcd2011-10-05 20:18:12 -0700299 public String getAlternateIconDescription() {
300 if (mAlternateIconDescriptionRes == 0) return null;
301 return mContext.getResources().getString(mAlternateIconDescriptionRes);
302 }
303
304 @Override
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800305 public Intent getIntent() {
306 return mIntent;
307 }
308
Daniel Lehmannedb576a2011-07-27 16:45:13 -0700309 @Override
310 public Intent getAlternateIntent() {
311 return mAlternateIntent;
312 }
313
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800314 @Override
315 public boolean collapseWith(Action other) {
316 if (!shouldCollapseWith(other)) {
317 return false;
318 }
319 return true;
320 }
321
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800322 @Override
323 public boolean shouldCollapseWith(Action t) {
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 }
Daniel Lehmann69fad282011-08-28 21:15:27 -0700331 DataAction that = (DataAction)t;
Dave Santoro01a9fac2011-09-15 16:08:29 -0700332 if (!ContactsUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) {
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800333 return false;
334 }
Daniel Lehmann69fad282011-08-28 21:15:27 -0700335 if (!TextUtils.equals(mMimeType, that.mMimeType)
336 || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) {
Daniel Lehmannaf8e3862010-11-19 15:38:44 -0800337 return false;
338 }
339 return true;
340 }
341}