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.common.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.net.Uri;
     27 import android.os.Bundle;
     28 import android.os.Environment;
     29 import android.os.Handler;
     30 import android.os.IBinder;
     31 import android.os.Message;
     32 import android.os.Messenger;
     33 import android.text.TextUtils;
     34 import android.util.Log;
     35 
     36 import com.android.contacts.common.R;
     37 
     38 import java.io.File;
     39 
     40 /**
     41  * Shows a dialog confirming the export and asks actual vCard export to {@link VCardService}
     42  *
     43  * This Activity first connects to VCardService and ask an available file name and shows it to
     44  * a user. After the user's confirmation, it send export request with the file name, assuming the
     45  * file name is not reserved yet.
     46  */
     47 public class ExportVCardActivity extends Activity implements ServiceConnection,
     48         DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     49     private static final String LOG_TAG = "VCardExport";
     50     private static final boolean DEBUG = VCardService.DEBUG;
     51 
     52     /**
     53      * Handler used when some Message has come from {@link VCardService}.
     54      */
     55     private class IncomingHandler extends Handler {
     56         @Override
     57         public void handleMessage(Message msg) {
     58             if (DEBUG) Log.d(LOG_TAG, "IncomingHandler received message.");
     59 
     60             if (msg.arg1 != 0) {
     61                 Log.i(LOG_TAG, "Message returned from vCard server contains error code.");
     62                 if (msg.obj != null) {
     63                     mErrorReason = (String)msg.obj;
     64                 }
     65                 showDialog(msg.arg1);
     66                 return;
     67             }
     68 
     69             switch (msg.what) {
     70             case VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION:
     71                 if (msg.obj == null) {
     72                     Log.w(LOG_TAG, "Message returned from vCard server doesn't contain valid path");
     73                     mErrorReason = getString(R.string.fail_reason_unknown);
     74                     showDialog(R.id.dialog_fail_to_export_with_reason);
     75                 } else {
     76                     mTargetFileName = (String)msg.obj;
     77                     if (TextUtils.isEmpty(mTargetFileName)) {
     78                         Log.w(LOG_TAG, "Destination file name coming from vCard service is empty.");
     79                         mErrorReason = getString(R.string.fail_reason_unknown);
     80                         showDialog(R.id.dialog_fail_to_export_with_reason);
     81                     } else {
     82                         if (DEBUG) {
     83                             Log.d(LOG_TAG,
     84                                     String.format("Target file name is set (%s). " +
     85                                             "Show confirmation dialog", mTargetFileName));
     86                         }
     87                         showDialog(R.id.dialog_export_confirmation);
     88                     }
     89                 }
     90                 break;
     91             default:
     92                 Log.w(LOG_TAG, "Unknown message type: " + msg.what);
     93                 super.handleMessage(msg);
     94             }
     95         }
     96     }
     97 
     98     /**
     99      * True when this Activity is connected to {@link VCardService}.
    100      *
    101      * Should be touched inside synchronized block.
    102      */
    103     private boolean mConnected;
    104 
    105     /**
    106      * True when users need to do something and this Activity should not disconnect from
    107      * VCardService. False when all necessary procedures are done (including sending export request)
    108      * or there's some error occured.
    109      */
    110     private volatile boolean mProcessOngoing = true;
    111 
    112     private VCardService mService;
    113     private final Messenger mIncomingMessenger = new Messenger(new IncomingHandler());
    114 
    115     // Used temporarily when asking users to confirm the file name
    116     private String mTargetFileName;
    117 
    118     // String for storing error reason temporarily.
    119     private String mErrorReason;
    120 
    121     private class ExportConfirmationListener implements DialogInterface.OnClickListener {
    122         private final Uri mDestinationUri;
    123 
    124         public ExportConfirmationListener(String path) {
    125             this(Uri.parse("file://" + path));
    126         }
    127 
    128         public ExportConfirmationListener(Uri uri) {
    129             mDestinationUri = uri;
    130         }
    131 
    132         public void onClick(DialogInterface dialog, int which) {
    133             if (which == DialogInterface.BUTTON_POSITIVE) {
    134                 if (DEBUG) {
    135                     Log.d(LOG_TAG,
    136                             String.format("Try sending export request (uri: %s)", mDestinationUri));
    137                 }
    138                 final ExportRequest request = new ExportRequest(mDestinationUri);
    139                 // The connection object will call finish().
    140                 mService.handleExportRequest(request, new NotificationImportExportListener(
    141                         ExportVCardActivity.this));
    142             }
    143             unbindAndFinish();
    144         }
    145     }
    146 
    147     @Override
    148     protected void onCreate(Bundle bundle) {
    149         super.onCreate(bundle);
    150 
    151         // Check directory is available.
    152         if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    153             Log.w(LOG_TAG, "External storage is in state " + Environment.getExternalStorageState() +
    154                     ". Cancelling export");
    155             showDialog(R.id.dialog_sdcard_not_found);
    156             return;
    157         }
    158 
    159         final File targetDirectory = Environment.getExternalStorageDirectory();
    160         if (!(targetDirectory.exists() &&
    161                 targetDirectory.isDirectory() &&
    162                 targetDirectory.canRead()) &&
    163                 !targetDirectory.mkdirs()) {
    164             showDialog(R.id.dialog_sdcard_not_found);
    165             return;
    166         }
    167 
    168         final String callingActivity = getIntent().getExtras()
    169                 .getString(VCardCommonArguments.ARG_CALLING_ACTIVITY);
    170         Intent intent = new Intent(this, VCardService.class);
    171         intent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY, callingActivity);
    172 
    173         if (startService(intent) == null) {
    174             Log.e(LOG_TAG, "Failed to start vCard service");
    175             mErrorReason = getString(R.string.fail_reason_unknown);
    176             showDialog(R.id.dialog_fail_to_export_with_reason);
    177             return;
    178         }
    179 
    180         if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) {
    181             Log.e(LOG_TAG, "Failed to connect to vCard service.");
    182             mErrorReason = getString(R.string.fail_reason_unknown);
    183             showDialog(R.id.dialog_fail_to_export_with_reason);
    184         }
    185         // Continued to onServiceConnected()
    186     }
    187 
    188     @Override
    189     public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
    190         if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
    191         mConnected = true;
    192         mService = ((VCardService.MyBinder) binder).getService();
    193         mService.handleRequestAvailableExportDestination(mIncomingMessenger);
    194         // Wait until MSG_SET_AVAILABLE_EXPORT_DESTINATION message is available.
    195     }
    196 
    197     // Use synchronized since we don't want to call unbindAndFinish() just after this call.
    198     @Override
    199     public synchronized void onServiceDisconnected(ComponentName name) {
    200         if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
    201         mService = null;
    202         mConnected = false;
    203         if (mProcessOngoing) {
    204             // Unexpected disconnect event.
    205             Log.w(LOG_TAG, "Disconnected from service during the process ongoing.");
    206             mErrorReason = getString(R.string.fail_reason_unknown);
    207             showDialog(R.id.dialog_fail_to_export_with_reason);
    208         }
    209     }
    210 
    211     @Override
    212     protected Dialog onCreateDialog(int id, Bundle bundle) {
    213         switch (id) {
    214             case R.id.dialog_export_confirmation: {
    215                 return new AlertDialog.Builder(this)
    216                         .setTitle(R.string.confirm_export_title)
    217                         .setMessage(getString(R.string.confirm_export_message, mTargetFileName))
    218                         .setPositiveButton(android.R.string.ok,
    219                                 new ExportConfirmationListener(mTargetFileName))
    220                         .setNegativeButton(android.R.string.cancel, this)
    221                         .setOnCancelListener(this)
    222                         .create();
    223             }
    224             case R.string.fail_reason_too_many_vcard: {
    225                 mProcessOngoing = false;
    226                 return new AlertDialog.Builder(this)
    227                         .setTitle(R.string.exporting_contact_failed_title)
    228                         .setMessage(getString(R.string.exporting_contact_failed_message,
    229                                 getString(R.string.fail_reason_too_many_vcard)))
    230                         .setPositiveButton(android.R.string.ok, this)
    231                         .create();
    232             }
    233             case R.id.dialog_fail_to_export_with_reason: {
    234                 mProcessOngoing = false;
    235                 return new AlertDialog.Builder(this)
    236                         .setTitle(R.string.exporting_contact_failed_title)
    237                         .setMessage(getString(R.string.exporting_contact_failed_message,
    238                                 mErrorReason != null ? mErrorReason :
    239                                         getString(R.string.fail_reason_unknown)))
    240                         .setPositiveButton(android.R.string.ok, this)
    241                         .setOnCancelListener(this)
    242                         .create();
    243             }
    244             case R.id.dialog_sdcard_not_found: {
    245                 mProcessOngoing = false;
    246                 return new AlertDialog.Builder(this)
    247                         .setIconAttribute(android.R.attr.alertDialogIcon)
    248                         .setMessage(R.string.no_sdcard_message)
    249                         .setPositiveButton(android.R.string.ok, this).create();
    250             }
    251         }
    252         return super.onCreateDialog(id, bundle);
    253     }
    254 
    255     @Override
    256     protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
    257         if (id == R.id.dialog_fail_to_export_with_reason) {
    258             ((AlertDialog)dialog).setMessage(mErrorReason);
    259         } else if (id == R.id.dialog_export_confirmation) {
    260             ((AlertDialog)dialog).setMessage(
    261                     getString(R.string.confirm_export_message, mTargetFileName));
    262         } else {
    263             super.onPrepareDialog(id, dialog, args);
    264         }
    265     }
    266 
    267     @Override
    268     protected void onStop() {
    269         super.onStop();
    270 
    271         if (!isFinishing()) {
    272             unbindAndFinish();
    273         }
    274     }
    275 
    276     @Override
    277     public void onClick(DialogInterface dialog, int which) {
    278         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
    279         unbindAndFinish();
    280     }
    281 
    282     @Override
    283     public void onCancel(DialogInterface dialog) {
    284         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called");
    285         mProcessOngoing = false;
    286         unbindAndFinish();
    287     }
    288 
    289     @Override
    290     public void unbindService(ServiceConnection conn) {
    291         mProcessOngoing = false;
    292         super.unbindService(conn);
    293     }
    294 
    295     private synchronized void unbindAndFinish() {
    296         if (mConnected) {
    297             unbindService(this);
    298             mConnected = false;
    299         }
    300         finish();
    301     }
    302 }
    303