• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
handleMessage(Message msg)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 
ExportConfirmationListener(String path)124         public ExportConfirmationListener(String path) {
125             this(Uri.parse("file://" + path));
126         }
127 
ExportConfirmationListener(Uri uri)128         public ExportConfirmationListener(Uri uri) {
129             mDestinationUri = uri;
130         }
131 
onClick(DialogInterface dialog, int which)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
onCreate(Bundle bundle)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
onServiceConnected(ComponentName name, IBinder binder)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
onServiceDisconnected(ComponentName name)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
onCreateDialog(int id, Bundle bundle)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
onPrepareDialog(int id, Dialog dialog, Bundle args)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
onStop()268     protected void onStop() {
269         super.onStop();
270 
271         if (!isFinishing()) {
272             unbindAndFinish();
273         }
274     }
275 
276     @Override
onClick(DialogInterface dialog, int which)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
onCancel(DialogInterface dialog)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
unbindService(ServiceConnection conn)290     public void unbindService(ServiceConnection conn) {
291         mProcessOngoing = false;
292         super.unbindService(conn);
293     }
294 
unbindAndFinish()295     private synchronized void unbindAndFinish() {
296         if (mConnected) {
297             unbindService(this);
298             mConnected = false;
299         }
300         finish();
301     }
302 }
303