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