ExportVCardActivity.java revision 62152c3097758e7084eed393556b86bd2fd795b7
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 android.app.Activity; 19import android.app.AlertDialog; 20import android.app.Dialog; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.net.Uri; 27import android.os.Bundle; 28import android.os.Environment; 29import android.os.Handler; 30import android.os.IBinder; 31import android.os.Message; 32import android.os.Messenger; 33import android.text.TextUtils; 34import android.util.Log; 35 36import com.android.contacts.R; 37 38import 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 */ 47public 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 Intent intent = new Intent(this, VCardService.class); 169 170 if (startService(intent) == null) { 171 Log.e(LOG_TAG, "Failed to start vCard service"); 172 mErrorReason = getString(R.string.fail_reason_unknown); 173 showDialog(R.id.dialog_fail_to_export_with_reason); 174 return; 175 } 176 177 if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) { 178 Log.e(LOG_TAG, "Failed to connect to vCard service."); 179 mErrorReason = getString(R.string.fail_reason_unknown); 180 showDialog(R.id.dialog_fail_to_export_with_reason); 181 } 182 // Continued to onServiceConnected() 183 } 184 185 @Override 186 public synchronized void onServiceConnected(ComponentName name, IBinder binder) { 187 if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name"); 188 mConnected = true; 189 mService = ((VCardService.MyBinder) binder).getService(); 190 mService.handleRequestAvailableExportDestination(mIncomingMessenger); 191 // Wait until MSG_SET_AVAILABLE_EXPORT_DESTINATION message is available. 192 } 193 194 // Use synchronized since we don't want to call unbindAndFinish() just after this call. 195 @Override 196 public synchronized void onServiceDisconnected(ComponentName name) { 197 if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()"); 198 mService = null; 199 mConnected = false; 200 if (mProcessOngoing) { 201 // Unexpected disconnect event. 202 Log.w(LOG_TAG, "Disconnected from service during the process ongoing."); 203 mErrorReason = getString(R.string.fail_reason_unknown); 204 showDialog(R.id.dialog_fail_to_export_with_reason); 205 } 206 } 207 208 @Override 209 protected Dialog onCreateDialog(int id, Bundle bundle) { 210 switch (id) { 211 case R.id.dialog_export_confirmation: { 212 return new AlertDialog.Builder(this) 213 .setTitle(R.string.confirm_export_title) 214 .setMessage(getString(R.string.confirm_export_message, mTargetFileName)) 215 .setPositiveButton(android.R.string.ok, 216 new ExportConfirmationListener(mTargetFileName)) 217 .setNegativeButton(android.R.string.cancel, this) 218 .setOnCancelListener(this) 219 .create(); 220 } 221 case R.string.fail_reason_too_many_vcard: { 222 mProcessOngoing = false; 223 return new AlertDialog.Builder(this) 224 .setTitle(R.string.exporting_contact_failed_title) 225 .setMessage(getString(R.string.exporting_contact_failed_message, 226 getString(R.string.fail_reason_too_many_vcard))) 227 .setPositiveButton(android.R.string.ok, this) 228 .create(); 229 } 230 case R.id.dialog_fail_to_export_with_reason: { 231 mProcessOngoing = false; 232 return new AlertDialog.Builder(this) 233 .setTitle(R.string.exporting_contact_failed_title) 234 .setMessage(getString(R.string.exporting_contact_failed_message, 235 mErrorReason != null ? mErrorReason : 236 getString(R.string.fail_reason_unknown))) 237 .setPositiveButton(android.R.string.ok, this) 238 .setOnCancelListener(this) 239 .create(); 240 } 241 case R.id.dialog_sdcard_not_found: { 242 mProcessOngoing = false; 243 return new AlertDialog.Builder(this) 244 .setIconAttribute(android.R.attr.alertDialogIcon) 245 .setMessage(R.string.no_sdcard_message) 246 .setPositiveButton(android.R.string.ok, this).create(); 247 } 248 } 249 return super.onCreateDialog(id, bundle); 250 } 251 252 @Override 253 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { 254 if (id == R.id.dialog_fail_to_export_with_reason) { 255 ((AlertDialog)dialog).setMessage(mErrorReason); 256 } else if (id == R.id.dialog_export_confirmation) { 257 ((AlertDialog)dialog).setMessage( 258 getString(R.string.confirm_export_message, mTargetFileName)); 259 } else { 260 super.onPrepareDialog(id, dialog, args); 261 } 262 } 263 264 @Override 265 protected void onStop() { 266 super.onStop(); 267 268 if (!isFinishing()) { 269 unbindAndFinish(); 270 } 271 } 272 273 @Override 274 public void onClick(DialogInterface dialog, int which) { 275 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called"); 276 unbindAndFinish(); 277 } 278 279 @Override 280 public void onCancel(DialogInterface dialog) { 281 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called"); 282 mProcessOngoing = false; 283 unbindAndFinish(); 284 } 285 286 @Override 287 public void unbindService(ServiceConnection conn) { 288 mProcessOngoing = false; 289 super.unbindService(conn); 290 } 291 292 private synchronized void unbindAndFinish() { 293 if (mConnected) { 294 unbindService(this); 295 mConnected = false; 296 } 297 finish(); 298 } 299} 300