• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.contacts.common.vcard;
18 
19 import android.app.Activity;
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.ServiceConnection;
26 import android.net.Uri;
27 import android.nfc.NdefMessage;
28 import android.nfc.NdefRecord;
29 import android.nfc.NfcAdapter;
30 import android.os.AsyncTask;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.provider.ContactsContract.RawContacts;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import com.android.contacts.common.R;
39 import com.android.contacts.common.activity.RequestPermissionsActivity;
40 import com.android.contacts.common.model.AccountTypeManager;
41 import com.android.contacts.common.model.account.AccountWithDataSet;
42 import com.android.contacts.common.util.ImplicitIntentsUtil;
43 import com.android.vcard.VCardEntry;
44 import com.android.vcard.VCardEntryCounter;
45 import com.android.vcard.VCardParser;
46 import com.android.vcard.VCardParser_V21;
47 import com.android.vcard.VCardParser_V30;
48 import com.android.vcard.VCardSourceDetector;
49 import com.android.vcard.exception.VCardException;
50 import com.android.vcard.exception.VCardNestedException;
51 import com.android.vcard.exception.VCardVersionException;
52 
53 import java.io.ByteArrayInputStream;
54 import java.io.IOException;
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 public class NfcImportVCardActivity extends Activity implements ServiceConnection,
59         VCardImportExportListener {
60     private static final String TAG = "NfcImportVCardActivity";
61 
62     private static final int SELECT_ACCOUNT = 1;
63 
64     private NdefRecord mRecord;
65     private AccountWithDataSet mAccount;
66     private Handler mHandler = new Handler();
67 
68     /**
69      * Notification id used when error happened before sending an import request to VCardServer.
70      */
71     private static final int FAILURE_NOTIFICATION_ID = 1;
72 
73     /* package */ class ImportTask extends AsyncTask<VCardService, Void, ImportRequest> {
74         @Override
doInBackground(VCardService... services)75         public ImportRequest doInBackground(VCardService... services) {
76             ImportRequest request = createImportRequest();
77             if (request == null) {
78                 return null;
79             }
80 
81             ArrayList<ImportRequest> requests = new ArrayList<ImportRequest>();
82             requests.add(request);
83             services[0].handleImportRequest(requests, NfcImportVCardActivity.this);
84             return request;
85         }
86 
87         @Override
onCancelled()88         public void onCancelled() {
89             unbindService(NfcImportVCardActivity.this);
90         }
91 
92         @Override
onPostExecute(ImportRequest request)93         public void onPostExecute(ImportRequest request) {
94             if (request == null) {
95                 // Finish the activity in case of error so it doesn't stay in view.
96                 finish();
97             }
98             unbindService(NfcImportVCardActivity.this);
99         }
100     }
101 
createImportRequest()102     /* package */ ImportRequest createImportRequest() {
103         VCardParser parser;
104         VCardEntryCounter counter = null;
105         VCardSourceDetector detector = null;
106         int vcardVersion = ImportVCardActivity.VCARD_VERSION_V21;
107         try {
108             ByteArrayInputStream is = new ByteArrayInputStream(mRecord.getPayload());
109             is.mark(0);
110             parser = new VCardParser_V21();
111             try {
112                 counter = new VCardEntryCounter();
113                 detector = new VCardSourceDetector();
114                 parser.addInterpreter(counter);
115                 parser.addInterpreter(detector);
116                 parser.parse(is);
117             } catch (VCardVersionException e1) {
118                 is.reset();
119                 vcardVersion = ImportVCardActivity.VCARD_VERSION_V30;
120                 parser = new VCardParser_V30();
121                 try {
122                     counter = new VCardEntryCounter();
123                     detector = new VCardSourceDetector();
124                     parser.addInterpreter(counter);
125                     parser.addInterpreter(detector);
126                     parser.parse(is);
127                 } catch (VCardVersionException e2) {
128                     Log.e(TAG, "vCard with unsupported version.");
129                     showFailureNotification(R.string.fail_reason_not_supported);
130                     return null;
131                 }
132             } finally {
133                 try {
134                     if (is != null) is.close();
135                 } catch (IOException e) {
136                 }
137             }
138         } catch (IOException e) {
139             Log.e(TAG, "Failed reading vCard data", e);
140             showFailureNotification(R.string.fail_reason_io_error);
141             return null;
142         } catch (VCardNestedException e) {
143             Log.w(TAG, "Nested Exception is found (it may be false-positive).");
144             // Go through without throwing the Exception, as we may be able to detect the
145             // version before it
146         } catch (VCardException e) {
147             Log.e(TAG, "Error parsing vCard", e);
148             showFailureNotification(R.string.fail_reason_not_supported);
149             return null;
150         }
151 
152         return new ImportRequest(mAccount, mRecord.getPayload(), null,
153                 getString(R.string.nfc_vcard_file_name), detector.getEstimatedType(),
154                 detector.getEstimatedCharset(), vcardVersion, counter.getCount());
155     }
156 
157     @Override
onServiceConnected(ComponentName name, IBinder binder)158     public void onServiceConnected(ComponentName name, IBinder binder) {
159         VCardService service = ((VCardService.MyBinder) binder).getService();
160         new ImportTask().execute(service);
161     }
162 
163     @Override
onServiceDisconnected(ComponentName name)164     public void onServiceDisconnected(ComponentName name) {
165         // Do nothing
166     }
167 
168     @Override
onCreate(Bundle bundle)169     protected void onCreate(Bundle bundle) {
170         super.onCreate(bundle);
171 
172         if (RequestPermissionsActivity.startPermissionActivity(this)) {
173             return;
174         }
175 
176         Intent intent = getIntent();
177         if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
178             Log.w(TAG, "Unknowon intent " + intent);
179             finish();
180             return;
181         }
182 
183         String type = intent.getType();
184         if (type == null ||
185                 (!"text/x-vcard".equals(type) && !"text/vcard".equals(type))) {
186             Log.w(TAG, "Not a vcard");
187             //setStatus(getString(R.string.fail_reason_not_supported));
188             finish();
189             return;
190         }
191         NdefMessage msg = (NdefMessage) intent.getParcelableArrayExtra(
192                 NfcAdapter.EXTRA_NDEF_MESSAGES)[0];
193         mRecord = msg.getRecords()[0];
194 
195         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
196         final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
197         if (accountList.size() == 0) {
198             mAccount = null;
199         } else if (accountList.size() == 1) {
200             mAccount = accountList.get(0);
201         } else {
202             startActivityForResult(new Intent(this, SelectAccountActivity.class), SELECT_ACCOUNT);
203             return;
204         }
205 
206         startImport();
207     }
208 
209     @Override
onActivityResult(int requestCode, int resultCode, Intent intent)210     public void onActivityResult(int requestCode, int resultCode, Intent intent) {
211         if (requestCode == SELECT_ACCOUNT) {
212             if (resultCode == RESULT_OK) {
213                 mAccount = new AccountWithDataSet(
214                         intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME),
215                         intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE),
216                         intent.getStringExtra(SelectAccountActivity.DATA_SET));
217                 startImport();
218             } else {
219                 finish();
220             }
221         }
222     }
223 
startImport()224     private void startImport() {
225         // We don't want the service finishes itself just after this connection.
226         Intent intent = new Intent(this, VCardService.class);
227         startService(intent);
228         bindService(intent, this, Context.BIND_AUTO_CREATE);
229     }
230 
231     @Override
onImportProcessed(ImportRequest request, int jobId, int sequence)232     public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
233         // do nothing
234     }
235 
236     @Override
onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount, int totalCount)237     public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
238             int totalCount) {
239         // do nothing
240     }
241 
242     @Override
onImportFinished(ImportRequest request, int jobId, Uri uri)243     public void onImportFinished(ImportRequest request, int jobId, Uri uri) {
244         if (isFinishing()) {
245             Log.i(TAG, "Late import -- ignoring");
246             return;
247         }
248 
249         if (uri != null) {
250             Uri contactUri = RawContacts.getContactLookupUri(getContentResolver(), uri);
251             Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
252             ImplicitIntentsUtil.startActivityInAppIfPossible(this, intent);
253             finish();
254         }
255     }
256 
257     @Override
onImportFailed(ImportRequest request)258     public void onImportFailed(ImportRequest request) {
259         if (isFinishing()) {
260             Log.i(TAG, "Late import failure -- ignoring");
261             return;
262         }
263         showFailureNotification(R.string.vcard_import_request_rejected_message);
264         finish();
265     }
266 
267     @Override
onImportCanceled(ImportRequest request, int jobId)268     public void onImportCanceled(ImportRequest request, int jobId) {
269         // do nothing
270     }
271 
272     @Override
onExportProcessed(ExportRequest request, int jobId)273     public void onExportProcessed(ExportRequest request, int jobId) {
274         // do nothing
275     }
276 
277     @Override
onExportFailed(ExportRequest request)278     public void onExportFailed(ExportRequest request) {
279         // do nothing
280     }
281 
282     @Override
onCancelRequest(CancelRequest request, int type)283     public void onCancelRequest(CancelRequest request, int type) {
284         // do nothing
285     }
286 
287     @Override
onComplete()288     public void onComplete() {
289         // do nothing
290     }
291 
showFailureNotification(int reasonId)292     /* package */ void showFailureNotification(int reasonId) {
293         final NotificationManager notificationManager =
294                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
295         final Notification notification =
296                 NotificationImportExportListener.constructImportFailureNotification(
297                         this,
298                         getString(reasonId));
299         notificationManager.notify(NotificationImportExportListener.FAILURE_NOTIFICATION_TAG,
300                 FAILURE_NOTIFICATION_ID, notification);
301         mHandler.post(new Runnable() {
302             @Override
303             public void run() {
304                 Toast.makeText(NfcImportVCardActivity.this,
305                         getString(R.string.vcard_import_failed), Toast.LENGTH_LONG).show();
306             }
307         });
308     }
309 }
310