ExportVCardActivity.java revision 783a09a8770f4322a45cee456adefbbc71218ece
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 */ 16package com.android.contacts.vcard; 17 18import com.android.contacts.R; 19import com.android.vcard.VCardComposer; 20 21import android.app.Activity; 22import android.app.AlertDialog; 23import android.app.Dialog; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.content.ServiceConnection; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.IBinder; 33import android.os.Message; 34import android.os.Messenger; 35import android.os.RemoteException; 36import android.text.TextUtils; 37import android.util.Log; 38 39import 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 */ 48public 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 Messenger mOutgoingMessenger; 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 if (trySend(Message.obtain(null, VCardService.MSG_EXPORT_REQUEST, request))) { 142 Log.i(LOG_TAG, "Successfully sent export request. Finish itself"); 143 unbindAndFinish(); 144 } 145 } 146 } 147 } 148 149 @Override 150 protected void onCreate(Bundle bundle) { 151 super.onCreate(bundle); 152 153 // Check directory is available. 154 final File targetDirectory = new File(getString(R.string.config_export_dir)); 155 if (!(targetDirectory.exists() && 156 targetDirectory.isDirectory() && 157 targetDirectory.canRead()) && 158 !targetDirectory.mkdirs()) { 159 showDialog(R.id.dialog_sdcard_not_found); 160 return; 161 } 162 163 if (startService(new Intent(this, VCardService.class)) == null) { 164 Log.e(LOG_TAG, "Failed to start vCard service"); 165 mErrorReason = getString(R.string.fail_reason_unknown); 166 showDialog(R.id.dialog_fail_to_export_with_reason); 167 return; 168 } 169 170 if (!bindService(new Intent(this, VCardService.class), this, Context.BIND_AUTO_CREATE)) { 171 Log.e(LOG_TAG, "Failed to connect to vCard service."); 172 mErrorReason = getString(R.string.fail_reason_unknown); 173 showDialog(R.id.dialog_fail_to_export_with_reason); 174 } 175 // Continued to onServiceConnected() 176 } 177 178 @Override 179 public synchronized void onServiceConnected(ComponentName name, IBinder service) { 180 if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name"); 181 mConnected = true; 182 mOutgoingMessenger = new Messenger(service); 183 final Message message = 184 Message.obtain(null, VCardService.MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION); 185 message.replyTo = mIncomingMessenger; 186 trySend(message); 187 // Wait until MSG_SET_AVAILABLE_EXPORT_DESTINATION message is available. 188 } 189 190 // Use synchronized since we don't want to call unbindAndFinish() just after this call. 191 @Override 192 public synchronized void onServiceDisconnected(ComponentName name) { 193 if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()"); 194 mOutgoingMessenger = null; 195 mConnected = false; 196 if (mProcessOngoing) { 197 // Unexpected disconnect event. 198 Log.w(LOG_TAG, "Disconnected from service during the process ongoing."); 199 mErrorReason = getString(R.string.fail_reason_unknown); 200 showDialog(R.id.dialog_fail_to_export_with_reason); 201 } 202 } 203 204 @Override 205 protected Dialog onCreateDialog(int id, Bundle bundle) { 206 switch (id) { 207 case R.id.dialog_export_confirmation: { 208 return new AlertDialog.Builder(this) 209 .setTitle(R.string.confirm_export_title) 210 .setMessage(getString(R.string.confirm_export_message, mTargetFileName)) 211 .setPositiveButton(android.R.string.ok, 212 new ExportConfirmationListener(mTargetFileName)) 213 .setNegativeButton(android.R.string.cancel, this) 214 .setOnCancelListener(this) 215 .create(); 216 } 217 case R.string.fail_reason_too_many_vcard: { 218 mProcessOngoing = false; 219 return new AlertDialog.Builder(this) 220 .setTitle(R.string.exporting_contact_failed_title) 221 .setMessage(getString(R.string.exporting_contact_failed_message, 222 getString(R.string.fail_reason_too_many_vcard))) 223 .setPositiveButton(android.R.string.ok, this) 224 .create(); 225 } 226 case R.id.dialog_fail_to_export_with_reason: { 227 mProcessOngoing = false; 228 return new AlertDialog.Builder(this) 229 .setTitle(R.string.exporting_contact_failed_title) 230 .setMessage(getString(R.string.exporting_contact_failed_message, 231 mErrorReason != null ? mErrorReason : 232 getString(R.string.fail_reason_unknown))) 233 .setPositiveButton(android.R.string.ok, this) 234 .setOnCancelListener(this) 235 .create(); 236 } 237 case R.id.dialog_sdcard_not_found: { 238 mProcessOngoing = false; 239 return new AlertDialog.Builder(this) 240 .setTitle(R.string.no_sdcard_title) 241 .setIcon(android.R.drawable.ic_dialog_alert) 242 .setMessage(R.string.no_sdcard_message) 243 .setPositiveButton(android.R.string.ok, this).create(); 244 } 245 } 246 return super.onCreateDialog(id, bundle); 247 } 248 249 @Override 250 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { 251 if (id == R.id.dialog_fail_to_export_with_reason) { 252 ((AlertDialog)dialog).setMessage(mErrorReason); 253 } else if (id == R.id.dialog_export_confirmation) { 254 ((AlertDialog)dialog).setMessage( 255 getString(R.string.confirm_export_message, mTargetFileName)); 256 } else { 257 super.onPrepareDialog(id, dialog, args); 258 } 259 } 260 261 @Override 262 protected void onStop() { 263 super.onStop(); 264 265 if (!isFinishing()) { 266 unbindAndFinish(); 267 } 268 } 269 270 private boolean trySend(Message message) { 271 try { 272 mOutgoingMessenger.send(message); 273 return true; 274 } catch (RemoteException e) { 275 Log.e(LOG_TAG, "RemoteException is thrown when trying to send request"); 276 unbindService(this); 277 mErrorReason = getString(R.string.fail_reason_unknown); 278 showDialog(R.id.dialog_fail_to_export_with_reason); 279 return false; 280 } 281 } 282 283 @Override 284 public void onClick(DialogInterface dialog, int which) { 285 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called"); 286 unbindAndFinish(); 287 } 288 289 @Override 290 public void onCancel(DialogInterface dialog) { 291 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called"); 292 mProcessOngoing = false; 293 unbindAndFinish(); 294 } 295 296 @Override 297 public void unbindService(ServiceConnection conn) { 298 mProcessOngoing = false; 299 super.unbindService(conn); 300 } 301 302 private synchronized void unbindAndFinish() { 303 if (mConnected) { 304 unbindService(this); 305 mConnected = false; 306 } 307 finish(); 308 } 309}