• 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.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
handleMessage(Message msg)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 
ExportConfirmationListener(String path)125         public ExportConfirmationListener(String path) {
126             this(Uri.parse("file://" + path));
127         }
128 
ExportConfirmationListener(Uri uri)129         public ExportConfirmationListener(Uri uri) {
130             mDestinationUri = uri;
131         }
132 
onClick(DialogInterface dialog, int which)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
onCreate(Bundle bundle)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
onServiceConnected(ComponentName name, IBinder binder)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
onServiceDisconnected(ComponentName name)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
onCreateDialog(int id, Bundle bundle)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
onPrepareDialog(int id, Dialog dialog, Bundle args)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
onStop()260     protected void onStop() {
261         super.onStop();
262 
263         if (!isFinishing()) {
264             unbindAndFinish();
265         }
266     }
267 
268     @Override
onClick(DialogInterface dialog, int which)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
onCancel(DialogInterface dialog)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
unbindService(ServiceConnection conn)282     public void unbindService(ServiceConnection conn) {
283         mProcessOngoing = false;
284         super.unbindService(conn);
285     }
286 
unbindAndFinish()287     private synchronized void unbindAndFinish() {
288         if (mConnected) {
289             unbindService(this);
290             mConnected = false;
291         }
292         finish();
293     }
294 }