• 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.database.Cursor;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.provider.OpenableColumns;
31 import android.text.BidiFormatter;
32 import android.text.TextDirectionHeuristics;
33 import android.util.Log;
34 
35 import com.android.contacts.common.R;
36 import com.android.contacts.common.activity.RequestImportVCardPermissionsActivity;
37 
38 /**
39  * Shows a dialog confirming the export and asks actual vCard export to {@link VCardService}
40  *
41  * This Activity first connects to VCardService and ask an available file name and shows it to
42  * a user. After the user's confirmation, it send export request with the file name, assuming the
43  * file name is not reserved yet.
44  */
45 public class ExportVCardActivity extends Activity implements ServiceConnection,
46         DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
47     private static final String LOG_TAG = "VCardExport";
48     private static final boolean DEBUG = VCardService.DEBUG;
49     private static final int REQUEST_CREATE_DOCUMENT = 100;
50 
51     /**
52      * True when this Activity is connected to {@link VCardService}.
53      *
54      * Should be touched inside synchronized block.
55      */
56     private boolean mConnected;
57 
58     /**
59      * True when users need to do something and this Activity should not disconnect from
60      * VCardService. False when all necessary procedures are done (including sending export request)
61      * or there's some error occured.
62      */
63     private volatile boolean mProcessOngoing = true;
64 
65     private VCardService mService;
66     private static final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
67 
68     // String for storing error reason temporarily.
69     private String mErrorReason;
70 
71     @Override
onCreate(Bundle bundle)72     protected void onCreate(Bundle bundle) {
73         super.onCreate(bundle);
74 
75         if (RequestImportVCardPermissionsActivity.startPermissionActivity(this)) {
76             return;
77         }
78 
79         connectVCardService();
80     }
81 
connectVCardService()82     private void connectVCardService() {
83         final String callingActivity = getIntent().getExtras()
84                 .getString(VCardCommonArguments.ARG_CALLING_ACTIVITY);
85         Intent intent = new Intent(this, VCardService.class);
86         intent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY, callingActivity);
87 
88         if (startService(intent) == null) {
89             Log.e(LOG_TAG, "Failed to start vCard service");
90             mErrorReason = getString(R.string.fail_reason_unknown);
91             showDialog(R.id.dialog_fail_to_export_with_reason);
92             return;
93         }
94 
95         if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) {
96             Log.e(LOG_TAG, "Failed to connect to vCard service.");
97             mErrorReason = getString(R.string.fail_reason_unknown);
98             showDialog(R.id.dialog_fail_to_export_with_reason);
99         }
100         // Continued to onServiceConnected()
101     }
102 
103     @Override
onActivityResult(int requestCode, int resultCode, Intent data)104     public void onActivityResult(int requestCode, int resultCode, Intent data) {
105         if (requestCode == REQUEST_CREATE_DOCUMENT) {
106             if (resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
107                 final Uri mTargetFileName = data.getData();
108                 if (DEBUG) Log.d(LOG_TAG, "exporting to " + mTargetFileName);
109                 final ExportRequest request = new ExportRequest(mTargetFileName);
110                 // The connection object will call finish().
111                 mService.handleExportRequest(request, new NotificationImportExportListener(
112                         ExportVCardActivity.this));
113             } else if (DEBUG) {
114                 Log.d(LOG_TAG, "create document cancelled or no data returned");
115             }
116             unbindAndFinish();
117         }
118     }
119 
120     @Override
onServiceConnected(ComponentName name, IBinder binder)121     public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
122         if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
123         mConnected = true;
124         mService = ((VCardService.MyBinder) binder).getService();
125 
126         // Have the user choose where vcards will be exported to
127         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
128         intent.addCategory(Intent.CATEGORY_OPENABLE);
129         intent.setType(VCardService.X_VCARD_MIME_TYPE);
130         intent.putExtra(Intent.EXTRA_TITLE, mBidiFormatter.unicodeWrap(
131                 getString(R.string.exporting_vcard_filename), TextDirectionHeuristics.LTR));
132         startActivityForResult(intent, REQUEST_CREATE_DOCUMENT);
133     }
134 
135     // Use synchronized since we don't want to call unbindAndFinish() just after this call.
136     @Override
onServiceDisconnected(ComponentName name)137     public synchronized void onServiceDisconnected(ComponentName name) {
138         if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
139         mService = null;
140         mConnected = false;
141         if (mProcessOngoing) {
142             // Unexpected disconnect event.
143             Log.w(LOG_TAG, "Disconnected from service during the process ongoing.");
144             mErrorReason = getString(R.string.fail_reason_unknown);
145             showDialog(R.id.dialog_fail_to_export_with_reason);
146         }
147     }
148 
149     @Override
onCreateDialog(int id, Bundle bundle)150     protected Dialog onCreateDialog(int id, Bundle bundle) {
151         switch (id) {
152             case R.string.fail_reason_too_many_vcard: {
153                 mProcessOngoing = false;
154                 return new AlertDialog.Builder(this)
155                         .setTitle(R.string.exporting_contact_failed_title)
156                         .setMessage(getString(R.string.exporting_contact_failed_message,
157                                 getString(R.string.fail_reason_too_many_vcard)))
158                         .setPositiveButton(android.R.string.ok, this)
159                         .create();
160             }
161             case R.id.dialog_fail_to_export_with_reason: {
162                 mProcessOngoing = false;
163                 return new AlertDialog.Builder(this)
164                         .setTitle(R.string.exporting_contact_failed_title)
165                         .setMessage(getString(R.string.exporting_contact_failed_message,
166                                 mErrorReason != null ? mErrorReason :
167                                         getString(R.string.fail_reason_unknown)))
168                         .setPositiveButton(android.R.string.ok, this)
169                         .setOnCancelListener(this)
170                         .create();
171             }
172         }
173         return super.onCreateDialog(id, bundle);
174     }
175 
176     @Override
onPrepareDialog(int id, Dialog dialog, Bundle args)177     protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
178         if (id == R.id.dialog_fail_to_export_with_reason) {
179             ((AlertDialog)dialog).setMessage(mErrorReason);
180         } else {
181             super.onPrepareDialog(id, dialog, args);
182         }
183     }
184 
185     @Override
onClick(DialogInterface dialog, int which)186     public void onClick(DialogInterface dialog, int which) {
187         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
188         unbindAndFinish();
189     }
190 
191     @Override
onCancel(DialogInterface dialog)192     public void onCancel(DialogInterface dialog) {
193         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called");
194         mProcessOngoing = false;
195         unbindAndFinish();
196     }
197 
198     @Override
unbindService(ServiceConnection conn)199     public void unbindService(ServiceConnection conn) {
200         mProcessOngoing = false;
201         super.unbindService(conn);
202     }
203 
204     /**
205      * Returns the display name for the given openable Uri or null if it could not be resolved. */
getOpenableUriDisplayName(Context context, Uri uri)206     static String getOpenableUriDisplayName(Context context, Uri uri) {
207         if (uri == null) return null;
208         final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
209         try {
210             if (cursor != null && cursor.moveToFirst()) {
211                 return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
212             }
213         } finally {
214             if (cursor != null)  {
215                 cursor.close();
216             }
217         }
218         return null;
219     }
220 
unbindAndFinish()221     private synchronized void unbindAndFinish() {
222         if (mConnected) {
223             unbindService(this);
224             mConnected = false;
225         }
226         finish();
227     }
228 }
229