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