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