82f74d87f3662d68dcd3b67482fc0c8bbabf3b0d
[android/platform/packages/apps/Tag.git] / src / com / android / apps / tag / record / VCardRecord.java
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
17 package com.android.apps.tag.record;
18
19 import com.android.apps.tag.R;
20 import com.google.common.base.Preconditions;
21
22 import android.app.Activity;
23 import android.content.ActivityNotFoundException;
24 import android.content.ContentUris;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.content.res.AssetFileDescriptor;
30 import android.database.Cursor;
31 import android.graphics.drawable.Drawable;
32 import android.net.Uri;
33 import android.nfc.NdefRecord;
34 import android.os.AsyncTask;
35 import android.os.Parcel;
36 import android.os.Parcelable;
37 import android.provider.ContactsContract;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.View.OnClickListener;
42 import android.view.ViewGroup;
43 import android.widget.ImageView;
44 import android.widget.TextView;
45
46 import java.io.FileInputStream;
47 import java.io.FileNotFoundException;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.lang.ref.WeakReference;
51 import java.util.List;
52 import java.util.Locale;
53
54 /**
55  * VCard Ndef Record object
56  */
57 public class VCardRecord extends ParsedNdefRecord implements OnClickListener {
58     private static final String TAG = VCardRecord.class.getSimpleName();
59
60     public static final String RECORD_TYPE = "vcard";
61
62     private final byte[] mVCard;
63
64     private VCardRecord(byte[] content) {
65         mVCard = content;
66     }
67
68     @Override
69     public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
70
71         Uri uri = activity.getIntent().getData();
72         uri = Uri.withAppendedPath(uri, Integer.toString(offset));
73         uri = Uri.withAppendedPath(uri, "mime");
74
75         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
76         return RecordUtils.getViewsForIntent(activity, inflater, parent, this, intent,
77                 activity.getString(R.string.import_vcard));
78     }
79
80     @Override
81     public String getSnippet(Context context, Locale locale) {
82         return "text/x-vCard";
83     }
84
85     /**
86      * Returns a view in a list of record types for adding new records to a message.
87      */
88     public static View getAddView(Context context, LayoutInflater inflater, ViewGroup parent) {
89         ViewGroup root = (ViewGroup) inflater.inflate(
90                 R.layout.tag_add_record_list_item, parent, false);
91
92         Intent intent = new Intent(Intent.ACTION_PICK);
93         intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
94
95         PackageManager pm = context.getPackageManager();
96         List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
97         if (activities.isEmpty()) {
98             return null;
99         }
100
101         ResolveInfo info = activities.get(0);
102         ((ImageView) root.findViewById(R.id.image)).setImageDrawable(info.loadIcon(pm));
103         ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.contact));
104
105         root.setTag(new VCardRecordEditInfo(intent));
106         return root;
107     }
108
109     public static VCardRecord parse(NdefRecord record) {
110         MimeRecord underlyingRecord = MimeRecord.parse(record);
111
112         // TODO: Add support for other vcard mime types.
113         Preconditions.checkArgument("text/x-vCard".equals(underlyingRecord.getMimeType()));
114         return new VCardRecord(underlyingRecord.getContent());
115     }
116
117     public static NdefRecord newVCardRecord(byte[] data) {
118         return MimeRecord.newMimeRecord("text/x-vCard", data);
119     }
120
121     @Override
122     public void onClick(View view) {
123         RecordUtils.ClickInfo info = (RecordUtils.ClickInfo) view.getTag();
124         try {
125             info.activity.startActivity(info.intent);
126             info.activity.finish();
127         } catch (ActivityNotFoundException e) {
128             // The activity wansn't found for some reason. Don't crash, but don't do anything.
129             Log.e(TAG, "Failed to launch activity for intent " + info.intent, e);
130         }
131     }
132
133     public static boolean isVCard(NdefRecord record) {
134         try {
135             parse(record);
136             return true;
137         } catch (IllegalArgumentException e) {
138             return false;
139         }
140     }
141
142     private static class VCardRecordEditInfo extends RecordEditInfo {
143         private final Intent mIntent;
144         private Uri mLookupUri;
145
146         private WeakReference<View> mActiveView = null;
147
148         private String mCachedName = null;
149         private Drawable mCachedPhoto = null;
150         private byte[] mCachedValue = null;
151
152         private static final class CacheData {
153             public String mName;
154             public Drawable mPhoto;
155             public byte[] mVcard;
156         }
157
158         public VCardRecordEditInfo(Intent intent) {
159             super(RECORD_TYPE);
160             mIntent = intent;
161         }
162
163         protected VCardRecordEditInfo(Parcel parcel) {
164             super(parcel);
165             mIntent = parcel.readParcelable(null);
166             mLookupUri = parcel.readParcelable(null);
167         }
168
169         @Override
170         public Intent getPickIntent() {
171             return mIntent;
172         }
173
174         private void fetchValues(final Context context) {
175             if (mCachedValue != null) {
176                 bindView();
177                 return;
178             }
179
180             new AsyncTask<Uri, Void, CacheData>() {
181                 @Override
182                 protected CacheData doInBackground(Uri... params) {
183                     Cursor cursor = null;
184                     long id;
185                     String lookupKey = null;
186                     Uri lookupUri = params[0];
187                     CacheData result = new CacheData();
188                     try {
189                         String[] projection = {
190                                 ContactsContract.Contacts._ID,
191                                 ContactsContract.Contacts.LOOKUP_KEY,
192                                 ContactsContract.Contacts.DISPLAY_NAME
193                         };
194                         cursor = context.getContentResolver().query(
195                                 lookupUri, projection, null, null, null);
196                         cursor.moveToFirst();
197                         id = cursor.getLong(0);
198                         lookupKey = cursor.getString(1);
199                         result.mName = cursor.getString(2);
200
201                     } finally {
202                         if (cursor != null) {
203                             cursor.close();
204                             cursor = null;
205                         }
206                     }
207
208                     if (lookupKey == null) {
209                         // TODO: handle errors.
210                         return null;
211                     }
212
213                     // Note: the lookup key should already encoded.
214                     Uri vcardUri = Uri.withAppendedPath(
215                             ContactsContract.Contacts.CONTENT_VCARD_URI,
216                             lookupKey);
217
218                     AssetFileDescriptor descriptor;
219                     FileInputStream in = null;
220                     try {
221                         descriptor =  context.getContentResolver().openAssetFileDescriptor(
222                                 vcardUri, "r");
223                         result.mVcard = new byte[(int) descriptor.getLength()];
224
225                         in = descriptor.createInputStream();
226                         in.read(result.mVcard);
227                         in.close();
228                     } catch (FileNotFoundException e) {
229                         return null;
230                     } catch (IOException e) {
231                         return null;
232                     }
233
234                     Uri contactUri = ContentUris.withAppendedId(
235                             ContactsContract.Contacts.CONTENT_URI, id);
236                     InputStream photoIn = ContactsContract.Contacts.openContactPhotoInputStream(
237                             context.getContentResolver(), contactUri);
238                     if (photoIn != null) {
239                         result.mPhoto = Drawable.createFromStream(photoIn, contactUri.toString());
240                     }
241                     return result;
242                 }
243
244                 @Override
245                 protected void onPostExecute(CacheData data) {
246                     if (data == null) {
247                         return;
248                     }
249
250                     mCachedName = data.mName;
251                     mCachedValue = data.mVcard;
252                     mCachedPhoto = data.mPhoto;
253                     bindView();
254                 }
255             }.execute(mLookupUri);
256         }
257
258         @Override
259         public NdefRecord getValue() {
260             return (mCachedValue == null) ? null : VCardRecord.newVCardRecord(mCachedValue);
261         }
262
263         @Override
264         public void handlePickResult(Context context, Intent data) {
265             mLookupUri = data.getData();
266             mCachedValue = null;
267             mCachedName = null;
268             mCachedPhoto = null;
269         }
270
271         private void bindView() {
272             View view = (mActiveView == null) ? null : mActiveView.get();
273             if (view == null) {
274                 return;
275             }
276
277             if (mCachedPhoto != null) {
278                 ((ImageView) view.findViewById(R.id.photo)).setImageDrawable(mCachedPhoto);
279             }
280
281             if (mCachedName != null) {
282                 ((TextView) view.findViewById(R.id.display_name)).setText(mCachedName);
283             }
284         }
285
286         @Override
287         public View getEditView(
288                 Activity activity, LayoutInflater inflater,
289                 ViewGroup parent, EditCallbacks callbacks) {
290             View result = buildEditView(
291                     activity, inflater, R.layout.tag_edit_vcard, parent, callbacks);
292
293             mActiveView = new WeakReference<View>(result);
294             result.setOnClickListener(this);
295
296             // Show default contact photo until the data loads.
297             ((ImageView) result.findViewById(R.id.photo)).setImageDrawable(
298                     activity.getResources().getDrawable(R.drawable.default_contact_photo));
299
300             fetchValues(activity);
301             return result;
302         }
303
304         @Override
305         public void writeToParcel(Parcel out, int flags) {
306             super.writeToParcel(out, flags);
307             out.writeParcelable(mIntent, flags);
308             out.writeParcelable(mLookupUri, flags);
309         }
310
311         @SuppressWarnings("unused")
312         public static final Parcelable.Creator<VCardRecordEditInfo> CREATOR =
313                 new Parcelable.Creator<VCardRecordEditInfo>() {
314             @Override
315             public VCardRecordEditInfo createFromParcel(Parcel in) {
316                 return new VCardRecordEditInfo(in);
317             }
318
319             @Override
320             public VCardRecordEditInfo[] newArray(int size) {
321                 return new VCardRecordEditInfo[size];
322             }
323         };
324
325         @Override
326         public int describeContents() {
327             return 0;
328         }
329
330         @Override
331         public void onClick(View target) {
332             if (this == target.getTag()) {
333                 mCallbacks.startPickForRecord(this, mIntent);
334             } else {
335                 super.onClick(target);
336             }
337         }
338     }
339 }