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