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