blob: 1f1e21bcd55b456530414152491868ab893f9887 [file] [log] [blame]
Chiao Chengd80c4342012-12-03 17:15:58 -08001/*
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 */
Gary Mai69c182a2016-12-05 13:07:03 -080016package com.android.contacts.vcard;
Chiao Chengd80c4342012-12-03 17:15:58 -080017
Brian Attwellc2e912c2014-10-27 12:29:44 -070018import android.app.Activity;
Chiao Chengd80c4342012-12-03 17:15:58 -080019import 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;
Wenyi Wang276990a2016-03-01 17:17:14 -080026import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
Walter Jang7a243012015-07-09 10:19:35 -070028import android.database.Cursor;
Chiao Chengd80c4342012-12-03 17:15:58 -080029import android.net.Uri;
30import android.os.Bundle;
Chiao Chengd80c4342012-12-03 17:15:58 -080031import android.os.IBinder;
Walter Jang7a243012015-07-09 10:19:35 -070032import android.provider.OpenableColumns;
Brian Attwellf0a4cf62014-10-17 14:32:35 -070033import android.text.BidiFormatter;
34import android.text.TextDirectionHeuristics;
Chiao Chengd80c4342012-12-03 17:15:58 -080035import android.util.Log;
36
Arthur Wang3f6a2442016-12-05 14:51:59 -080037import com.android.contacts.R;
Gary Mai0a49afa2016-12-05 15:53:58 -080038import com.android.contacts.activities.RequestImportVCardPermissionsActivity;
Chiao Chengd80c4342012-12-03 17:15:58 -080039
Wenyi Wang276990a2016-03-01 17:17:14 -080040import java.util.List;
41
Chiao Chengd80c4342012-12-03 17:15:58 -080042/**
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 */
Brian Attwellc2e912c2014-10-27 12:29:44 -070049public class ExportVCardActivity extends Activity implements ServiceConnection,
Chiao Chengd80c4342012-12-03 17:15:58 -080050 DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
51 private static final String LOG_TAG = "VCardExport";
Wenyi Wang142a3442016-02-04 14:08:45 -080052 protected static final boolean DEBUG = VCardService.DEBUG;
Walter Jang1dc13a42015-07-01 11:26:07 -070053 private static final int REQUEST_CREATE_DOCUMENT = 100;
Chiao Chengd80c4342012-12-03 17:15:58 -080054
55 /**
56 * True when this Activity is connected to {@link VCardService}.
57 *
58 * Should be touched inside synchronized block.
59 */
Wenyi Wang142a3442016-02-04 14:08:45 -080060 protected boolean mConnected;
Chiao Chengd80c4342012-12-03 17:15:58 -080061
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
Wenyi Wang142a3442016-02-04 14:08:45 -080069 protected VCardService mService;
Brian Attwellf0a4cf62014-10-17 14:32:35 -070070 private static final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
Chiao Chengd80c4342012-12-03 17:15:58 -080071
Chiao Chengd80c4342012-12-03 17:15:58 -080072 // String for storing error reason temporarily.
73 private String mErrorReason;
74
Chiao Chengd80c4342012-12-03 17:15:58 -080075 @Override
76 protected void onCreate(Bundle bundle) {
77 super.onCreate(bundle);
78
Brian Attwell62f80072015-06-23 23:26:27 -070079 if (RequestImportVCardPermissionsActivity.startPermissionActivity(this)) {
80 return;
81 }
82
Wenyi Wang276990a2016-03-01 17:17:14 -080083 if (!hasExportIntentHandler()) {
84 Log.e(LOG_TAG, "Couldn't find export intent handler");
85 showErrorDialog();
86 return;
87 }
88
Walter Jang1dc13a42015-07-01 11:26:07 -070089 connectVCardService();
90 }
Chiao Chengd80c4342012-12-03 17:15:58 -080091
Walter Jang1dc13a42015-07-01 11:26:07 -070092 private void connectVCardService() {
Chiao Chengd80c4342012-12-03 17:15:58 -080093 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");
Wenyi Wang276990a2016-03-01 17:17:14 -0800100 showErrorDialog();
Chiao Chengd80c4342012-12-03 17:15:58 -0800101 return;
102 }
103
104 if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) {
105 Log.e(LOG_TAG, "Failed to connect to vCard service.");
Wenyi Wang276990a2016-03-01 17:17:14 -0800106 showErrorDialog();
Chiao Chengd80c4342012-12-03 17:15:58 -0800107 }
108 // Continued to onServiceConnected()
109 }
110
Wenyi Wang276990a2016-03-01 17:17:14 -0800111 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
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
127 private void showErrorDialog() {
128 mErrorReason = getString(R.string.fail_reason_unknown);
129 showDialog(R.id.dialog_fail_to_export_with_reason);
130 }
131
Chiao Chengd80c4342012-12-03 17:15:58 -0800132 @Override
Walter Jang1dc13a42015-07-01 11:26:07 -0700133 public void onActivityResult(int requestCode, int resultCode, Intent data) {
134 if (requestCode == REQUEST_CREATE_DOCUMENT) {
Jay Shrauner14c441b2015-11-18 13:07:37 -0800135 if (resultCode == Activity.RESULT_OK && mService != null &&
136 data != null && data.getData() != null) {
Wenyi Wang142a3442016-02-04 14:08:45 -0800137 final Uri targetFileName = data.getData();
138 if (DEBUG) Log.d(LOG_TAG, "exporting to " + targetFileName);
139 final ExportRequest request = new ExportRequest(targetFileName);
Walter Jang1dc13a42015-07-01 11:26:07 -0700140 // The connection object will call finish().
141 mService.handleExportRequest(request, new NotificationImportExportListener(
142 ExportVCardActivity.this));
143 } else if (DEBUG) {
Jay Shrauner14c441b2015-11-18 13:07:37 -0800144 if (mService == null) {
145 Log.d(LOG_TAG, "No vCard service.");
146 } else {
147 Log.d(LOG_TAG, "create document cancelled or no data returned");
148 }
Walter Jang1dc13a42015-07-01 11:26:07 -0700149 }
guanxiongliucca57302016-06-08 18:41:59 -0700150 finish();
Walter Jang1dc13a42015-07-01 11:26:07 -0700151 }
152 }
153
154 @Override
Chiao Chengd80c4342012-12-03 17:15:58 -0800155 public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
156 if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
157 mConnected = true;
158 mService = ((VCardService.MyBinder) binder).getService();
Walter Jang1dc13a42015-07-01 11:26:07 -0700159
160 // Have the user choose where vcards will be exported to
Wenyi Wang276990a2016-03-01 17:17:14 -0800161 startActivityForResult(getCreateDocIntent(), REQUEST_CREATE_DOCUMENT);
Chiao Chengd80c4342012-12-03 17:15:58 -0800162 }
163
guanxiongliucca57302016-06-08 18:41:59 -0700164 // Use synchronized since we don't want to call finish() just after this call.
Chiao Chengd80c4342012-12-03 17:15:58 -0800165 @Override
166 public synchronized void onServiceDisconnected(ComponentName name) {
167 if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
168 mService = null;
169 mConnected = false;
170 if (mProcessOngoing) {
171 // Unexpected disconnect event.
172 Log.w(LOG_TAG, "Disconnected from service during the process ongoing.");
Wenyi Wang276990a2016-03-01 17:17:14 -0800173 showErrorDialog();
Chiao Chengd80c4342012-12-03 17:15:58 -0800174 }
175 }
176
177 @Override
178 protected Dialog onCreateDialog(int id, Bundle bundle) {
Sailesh Nepal329f4272016-02-17 02:16:02 -0800179 if (id == R.id.dialog_fail_to_export_with_reason) {
180 mProcessOngoing = false;
181 return new AlertDialog.Builder(this)
182 .setTitle(R.string.exporting_contact_failed_title)
183 .setMessage(getString(R.string.exporting_contact_failed_message,
184 mErrorReason != null ? mErrorReason :
185 getString(R.string.fail_reason_unknown)))
186 .setPositiveButton(android.R.string.ok, this)
187 .setOnCancelListener(this)
188 .create();
Chiao Chengd80c4342012-12-03 17:15:58 -0800189 }
190 return super.onCreateDialog(id, bundle);
191 }
192
193 @Override
194 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
195 if (id == R.id.dialog_fail_to_export_with_reason) {
196 ((AlertDialog)dialog).setMessage(mErrorReason);
Chiao Chengd80c4342012-12-03 17:15:58 -0800197 } else {
198 super.onPrepareDialog(id, dialog, args);
199 }
200 }
201
202 @Override
Chiao Chengd80c4342012-12-03 17:15:58 -0800203 public void onClick(DialogInterface dialog, int which) {
204 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
guanxiongliucca57302016-06-08 18:41:59 -0700205 finish();
Chiao Chengd80c4342012-12-03 17:15:58 -0800206 }
207
208 @Override
209 public void onCancel(DialogInterface dialog) {
210 if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called");
211 mProcessOngoing = false;
guanxiongliucca57302016-06-08 18:41:59 -0700212 finish();
Chiao Chengd80c4342012-12-03 17:15:58 -0800213 }
214
215 @Override
216 public void unbindService(ServiceConnection conn) {
217 mProcessOngoing = false;
218 super.unbindService(conn);
219 }
220
guanxiongliucca57302016-06-08 18:41:59 -0700221 @Override
222 protected void onDestroy() {
223 if (mConnected) {
224 unbindService(this);
225 mConnected = false;
226 }
227 super.onDestroy();
228 }
229
Walter Jang7a243012015-07-09 10:19:35 -0700230 /**
231 * Returns the display name for the given openable Uri or null if it could not be resolved. */
232 static String getOpenableUriDisplayName(Context context, Uri uri) {
233 if (uri == null) return null;
234 final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
235 try {
236 if (cursor != null && cursor.moveToFirst()) {
237 return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
238 }
239 } finally {
240 if (cursor != null) {
241 cursor.close();
242 }
243 }
244 return null;
245 }
Chiao Chengd80c4342012-12-03 17:15:58 -0800246}