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