Home | History | Annotate | Download | only in vcard
      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 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.R;
     39 import com.android.contacts.activities.RequestPermissionsActivity;
     40 import com.android.contacts.model.AccountTypeManager;
     41 import com.android.contacts.model.account.AccountWithDataSet;
     42 import com.android.contacts.util.ImplicitIntentsUtil;
     43 import com.android.contactsbind.FeedbackHelper;
     44 import com.android.vcard.VCardEntry;
     45 import com.android.vcard.VCardEntryCounter;
     46 import com.android.vcard.VCardParser;
     47 import com.android.vcard.VCardParser_V21;
     48 import com.android.vcard.VCardParser_V30;
     49 import com.android.vcard.VCardSourceDetector;
     50 import com.android.vcard.exception.VCardException;
     51 import com.android.vcard.exception.VCardNestedException;
     52 import com.android.vcard.exception.VCardVersionException;
     53 
     54 import java.io.ByteArrayInputStream;
     55 import java.io.IOException;
     56 import java.util.ArrayList;
     57 import java.util.List;
     58 
     59 public class NfcImportVCardActivity extends Activity implements ServiceConnection,
     60         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;
     67     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;
     73 
     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) {
     95             if (request == null) {
     96                 // Finish the activity in case of error so it doesn't stay in view.
     97                 finish();
     98             }
     99             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) {
    129                     FeedbackHelper.sendFeedback(this, TAG, "vcard with unsupported version", e2);
    130                     showFailureNotification(R.string.fail_reason_not_supported);
    131                     return null;
    132                 }
    133             } finally {
    134                 try {
    135                     if (is != null) is.close();
    136                 } catch (IOException e) {
    137                 }
    138             }
    139         } catch (IOException e) {
    140             FeedbackHelper.sendFeedback(this, TAG, "Failed to read vcard data", e);
    141             showFailureNotification(R.string.fail_reason_io_error);
    142             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) {
    148             FeedbackHelper.sendFeedback(this, TAG, "Failed to parse vcard", e);
    149             showFailureNotification(R.string.fail_reason_not_supported);
    150             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 
    173         if (RequestPermissionsActivity.startPermissionActivityIfNeeded(this)) {
    174             return;
    175         }
    176 
    177         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.blockForWritableAccounts();
    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 Notification onImportProcessed(ImportRequest request, int jobId, int sequence) {
    234         return null;
    235     }
    236 
    237     @Override
    238     public Notification onImportParsed(ImportRequest request, int jobId, VCardEntry entry,
    239             int currentCount, int totalCount) {
    240         return null;
    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);
    253             ImplicitIntentsUtil.startActivityInAppIfPossible(this, intent);
    254             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         }
    264         showFailureNotification(R.string.vcard_import_request_rejected_message);
    265         finish();
    266     }
    267 
    268     @Override
    269     public void onImportCanceled(ImportRequest request, int jobId) {
    270         // do nothing
    271     }
    272 
    273     @Override
    274     public Notification onExportProcessed(ExportRequest request, int jobId) {
    275         return null;
    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     /* package */ void showFailureNotification(int reasonId) {
    289         final NotificationManager notificationManager =
    290                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    291         final Notification notification =
    292                 NotificationImportExportListener.constructImportFailureNotification(
    293                         this,
    294                         getString(reasonId));
    295         notificationManager.notify(NotificationImportExportListener.FAILURE_NOTIFICATION_TAG,
    296                 FAILURE_NOTIFICATION_ID, notification);
    297         mHandler.post(new Runnable() {
    298             @Override
    299             public void run() {
    300                 Toast.makeText(NfcImportVCardActivity.this,
    301                         getString(R.string.vcard_import_failed), Toast.LENGTH_LONG).show();
    302             }
    303         });
    304     }
    305 }
    306