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