Home | History | Annotate | Download | only in vcard
      1 /*
      2  * Copyright (C) 2009 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 package com.android.contacts.vcard;
     17 
     18 import android.app.Activity;
     19 import android.app.AlertDialog;
     20 import android.app.Dialog;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.ServiceConnection;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.ResolveInfo;
     28 import android.database.Cursor;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.os.IBinder;
     32 import android.provider.OpenableColumns;
     33 import android.text.BidiFormatter;
     34 import android.text.TextDirectionHeuristics;
     35 import android.util.Log;
     36 
     37 import com.android.contacts.R;
     38 import com.android.contacts.activities.RequestImportVCardPermissionsActivity;
     39 
     40 import java.util.List;
     41 
     42 /**
     43  * Shows a dialog confirming the export and asks actual vCard export to {@link VCardService}
     44  *
     45  * This Activity first connects to VCardService and ask an available file name and shows it to
     46  * a user. After the user's confirmation, it send export request with the file name, assuming the
     47  * file name is not reserved yet.
     48  */
     49 public class ExportVCardActivity extends Activity implements ServiceConnection,
     50         DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     51     private static final String LOG_TAG = "VCardExport";
     52     protected static final boolean DEBUG = VCardService.DEBUG;
     53     private static final int REQUEST_CREATE_DOCUMENT = 100;
     54 
     55     /**
     56      * True when this Activity is connected to {@link VCardService}.
     57      *
     58      * Should be touched inside synchronized block.
     59      */
     60     protected boolean mConnected;
     61 
     62     /**
     63      * True when users need to do something and this Activity should not disconnect from
     64      * VCardService. False when all necessary procedures are done (including sending export request)
     65      * or there's some error occured.
     66      */
     67     private volatile boolean mProcessOngoing = true;
     68 
     69     protected VCardService mService;
     70     private static final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
     71 
     72     // String for storing error reason temporarily.
     73     private String mErrorReason;
     74 
     75     @Override
     76     protected void onCreate(Bundle bundle) {
     77         super.onCreate(bundle);
     78 
     79         if (RequestImportVCardPermissionsActivity.startPermissionActivityIfNeeded(this)) {
     80             return;
     81         }
     82 
     83         if (!hasExportIntentHandler()) {
     84             Log.e(LOG_TAG, "Couldn't find export intent handler");
     85             showErrorDialog();
     86             return;
     87         }
     88 
     89         connectVCardService();
     90     }
     91 
     92     private void connectVCardService() {
     93         final String callingActivity = getIntent().getExtras()
     94                 .getString(VCardCommonArguments.ARG_CALLING_ACTIVITY);
     95         Intent intent = new Intent(this, VCardService.class);
     96         intent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY, callingActivity);
     97 
     98         if (startService(intent) == null) {
     99             Log.e(LOG_TAG, "Failed to start vCard service");
    100             showErrorDialog();
    101             return;
    102         }
    103 
    104         if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) {
    105             Log.e(LOG_TAG, "Failed to connect to vCard service.");
    106             showErrorDialog();
    107         }
    108         // Continued to onServiceConnected()
    109     }
    110 
    111     private boolean hasExportIntentHandler() {
    112         final Intent intent = getCreateDocIntent();
    113         final List<ResolveInfo> receivers = getPackageManager().queryIntentActivities(intent,
    114                 PackageManager.MATCH_DEFAULT_ONLY);
    115         return receivers != null && receivers.size() > 0;
    116     }
    117 
    118     private Intent getCreateDocIntent() {
    119         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    120         intent.addCategory(Intent.CATEGORY_OPENABLE);
    121         intent.setType(VCardService.X_VCARD_MIME_TYPE);
    122         intent.putExtra(Intent.EXTRA_TITLE, mBidiFormatter.unicodeWrap(
    123                 getString(R.string.exporting_vcard_filename), TextDirectionHeuristics.LTR));
    124         return intent;
    125     }
    126 
    127     private void showErrorDialog() {
    128         mErrorReason = getString(R.string.fail_reason_unknown);
    129         showDialog(R.id.dialog_fail_to_export_with_reason);
    130     }
    131 
    132     @Override
    133     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    134         if (requestCode == REQUEST_CREATE_DOCUMENT) {
    135             if (resultCode == Activity.RESULT_OK && mService != null &&
    136                     data != null && data.getData() != null) {
    137                 final Uri targetFileName = data.getData();
    138                 if (DEBUG) Log.d(LOG_TAG, "exporting to " + targetFileName);
    139                 final ExportRequest request = new ExportRequest(targetFileName);
    140                 // The connection object will call finish().
    141                 mService.handleExportRequest(request, new NotificationImportExportListener(
    142                         ExportVCardActivity.this));
    143             } else if (DEBUG) {
    144                 if (mService == null) {
    145                     Log.d(LOG_TAG, "No vCard service.");
    146                 } else {
    147                     Log.d(LOG_TAG, "create document cancelled or no data returned");
    148                 }
    149             }
    150             finish();
    151         }
    152     }
    153 
    154     @Override
    155     public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
    156         if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
    157         mConnected = true;
    158         mService = ((VCardService.MyBinder) binder).getService();
    159 
    160         // Have the user choose where vcards will be exported to
    161         startActivityForResult(getCreateDocIntent(), REQUEST_CREATE_DOCUMENT);
    162     }
    163 
    164     // Use synchronized since we don't want to call finish() just after this call.
    165     @Override
    166     public synchronized void onServiceDisconnected(ComponentName name) {
    167         if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
    168         mService = null;
    169         mConnected = false;
    170         if (mProcessOngoing) {
    171             // Unexpected disconnect event.
    172             Log.w(LOG_TAG, "Disconnected from service during the process ongoing.");
    173             showErrorDialog();
    174         }
    175     }
    176 
    177     @Override
    178     protected Dialog onCreateDialog(int id, Bundle bundle) {
    179         if (id == R.id.dialog_fail_to_export_with_reason) {
    180             mProcessOngoing = false;
    181             return new AlertDialog.Builder(this)
    182                     .setTitle(R.string.exporting_contact_failed_title)
    183                     .setMessage(getString(R.string.exporting_contact_failed_message,
    184                             mErrorReason != null ? mErrorReason :
    185                                     getString(R.string.fail_reason_unknown)))
    186                     .setPositiveButton(android.R.string.ok, this)
    187                     .setOnCancelListener(this)
    188                     .create();
    189         }
    190         return super.onCreateDialog(id, bundle);
    191     }
    192 
    193     @Override
    194     protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
    195         if (id == R.id.dialog_fail_to_export_with_reason) {
    196             ((AlertDialog)dialog).setMessage(mErrorReason);
    197         } else {
    198             super.onPrepareDialog(id, dialog, args);
    199         }
    200     }
    201 
    202     @Override
    203     public void onClick(DialogInterface dialog, int which) {
    204         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
    205         finish();
    206     }
    207 
    208     @Override
    209     public void onCancel(DialogInterface dialog) {
    210         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called");
    211         mProcessOngoing = false;
    212         finish();
    213     }
    214 
    215     @Override
    216     public void unbindService(ServiceConnection conn) {
    217         mProcessOngoing = false;
    218         super.unbindService(conn);
    219     }
    220 
    221     @Override
    222     protected void onDestroy() {
    223         if (mConnected) {
    224             unbindService(this);
    225             mConnected = false;
    226         }
    227         super.onDestroy();
    228     }
    229 
    230     /**
    231      * Returns the display name for the given openable Uri or null if it could not be resolved. */
    232     static String getOpenableUriDisplayName(Context context, Uri uri) {
    233         if (uri == null) return null;
    234         final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
    235         try {
    236             if (cursor != null && cursor.moveToFirst()) {
    237                 return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    238             }
    239         } finally {
    240             if (cursor != null)  {
    241                 cursor.close();
    242             }
    243         }
    244         return null;
    245     }
    246 }
    247