Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 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 | |
| 17 | package com.android.contacts.common.vcard; |
| 18 | |
| 19 | import android.app.Activity; |
| 20 | import android.app.AlertDialog; |
| 21 | import android.app.Dialog; |
| 22 | import android.app.Notification; |
| 23 | import android.app.NotificationManager; |
| 24 | import android.app.ProgressDialog; |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 25 | import android.content.ClipData; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 26 | import android.content.ComponentName; |
| 27 | import android.content.ContentResolver; |
| 28 | import android.content.Context; |
| 29 | import android.content.DialogInterface; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 30 | import android.content.Intent; |
| 31 | import android.content.ServiceConnection; |
| 32 | import android.database.Cursor; |
| 33 | import android.net.Uri; |
| 34 | import android.os.Bundle; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 35 | import android.os.Handler; |
| 36 | import android.os.IBinder; |
| 37 | import android.os.PowerManager; |
| 38 | import android.provider.OpenableColumns; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 39 | import android.text.TextUtils; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 40 | import android.util.Log; |
| 41 | import android.widget.Toast; |
| 42 | |
| 43 | import com.android.contacts.common.R; |
Brian Attwell | 46c6759 | 2015-05-11 17:20:52 -0700 | [diff] [blame] | 44 | import com.android.contacts.common.activity.RequestImportVCardPermissionsActivity; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 45 | import com.android.contacts.common.model.AccountTypeManager; |
| 46 | import com.android.contacts.common.model.account.AccountWithDataSet; |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 47 | import com.android.contactsbind.FeedbackHelper; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 48 | import com.android.vcard.VCardEntryCounter; |
| 49 | import com.android.vcard.VCardParser; |
| 50 | import com.android.vcard.VCardParser_V21; |
| 51 | import com.android.vcard.VCardParser_V30; |
| 52 | import com.android.vcard.VCardSourceDetector; |
| 53 | import com.android.vcard.exception.VCardException; |
| 54 | import com.android.vcard.exception.VCardNestedException; |
| 55 | import com.android.vcard.exception.VCardVersionException; |
| 56 | |
| 57 | import java.io.ByteArrayInputStream; |
| 58 | import java.io.File; |
| 59 | import java.io.IOException; |
| 60 | import java.io.InputStream; |
| 61 | import java.nio.ByteBuffer; |
| 62 | import java.nio.channels.Channels; |
| 63 | import java.nio.channels.ReadableByteChannel; |
| 64 | import java.nio.channels.WritableByteChannel; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 65 | import java.util.ArrayList; |
| 66 | import java.util.Arrays; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 67 | import java.util.List; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 68 | |
| 69 | /** |
| 70 | * The class letting users to import vCard. This includes the UI part for letting them select |
| 71 | * an Account and posssibly a file if there's no Uri is given from its caller Activity. |
| 72 | * |
| 73 | * Note that this Activity assumes that the instance is a "one-shot Activity", which will be |
| 74 | * finished (with the method {@link Activity#finish()}) after the import and never reuse |
| 75 | * any Dialog in the instance. So this code is careless about the management around managed |
| 76 | * dialogs stuffs (like how onCreateDialog() is used). |
| 77 | */ |
Walter Jang | 915ccdd | 2016-10-24 12:18:20 -0700 | [diff] [blame] | 78 | public class ImportVCardActivity extends Activity implements ImportVCardDialogFragment.Listener { |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 79 | private static final String LOG_TAG = "VCardImport"; |
| 80 | |
| 81 | private static final int SELECT_ACCOUNT = 0; |
| 82 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 83 | /* package */ final static int VCARD_VERSION_AUTO_DETECT = 0; |
| 84 | /* package */ final static int VCARD_VERSION_V21 = 1; |
| 85 | /* package */ final static int VCARD_VERSION_V30 = 2; |
| 86 | |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 87 | private static final int REQUEST_OPEN_DOCUMENT = 100; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 88 | |
| 89 | /** |
| 90 | * Notification id used when error happened before sending an import request to VCardServer. |
| 91 | */ |
| 92 | private static final int FAILURE_NOTIFICATION_ID = 1; |
| 93 | |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 94 | private static final String LOCAL_TMP_FILE_NAME_EXTRA = |
| 95 | "com.android.contacts.common.vcard.LOCAL_TMP_FILE_NAME"; |
| 96 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 97 | private static final String SOURCE_URI_DISPLAY_NAME = |
| 98 | "com.android.contacts.common.vcard.SOURCE_URI_DISPLAY_NAME"; |
| 99 | |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 100 | private static final String STORAGE_VCARD_URI_PREFIX = "file:///storage"; |
Tingting Wang | a6139f9 | 2015-11-30 12:44:27 -0800 | [diff] [blame] | 101 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 102 | private AccountWithDataSet mAccount; |
| 103 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 104 | private ProgressDialog mProgressDialogForCachingVCard; |
| 105 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 106 | private VCardCacheThread mVCardCacheThread; |
| 107 | private ImportRequestConnection mConnection; |
| 108 | /* package */ VCardImportExportListener mListener; |
| 109 | |
| 110 | private String mErrorMessage; |
| 111 | |
| 112 | private Handler mHandler = new Handler(); |
| 113 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 114 | // Runs on the UI thread. |
| 115 | private class DialogDisplayer implements Runnable { |
| 116 | private final int mResId; |
| 117 | public DialogDisplayer(int resId) { |
| 118 | mResId = resId; |
| 119 | } |
| 120 | public DialogDisplayer(String errorMessage) { |
| 121 | mResId = R.id.dialog_error_with_message; |
| 122 | mErrorMessage = errorMessage; |
| 123 | } |
| 124 | @Override |
| 125 | public void run() { |
| 126 | if (!isFinishing()) { |
| 127 | showDialog(mResId); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | private class CancelListener |
| 133 | implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { |
| 134 | @Override |
| 135 | public void onClick(DialogInterface dialog, int which) { |
| 136 | finish(); |
| 137 | } |
| 138 | @Override |
| 139 | public void onCancel(DialogInterface dialog) { |
| 140 | finish(); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | private CancelListener mCancelListener = new CancelListener(); |
| 145 | |
| 146 | private class ImportRequestConnection implements ServiceConnection { |
| 147 | private VCardService mService; |
| 148 | |
| 149 | public void sendImportRequest(final List<ImportRequest> requests) { |
| 150 | Log.i(LOG_TAG, "Send an import request"); |
| 151 | mService.handleImportRequest(requests, mListener); |
| 152 | } |
| 153 | |
| 154 | @Override |
| 155 | public void onServiceConnected(ComponentName name, IBinder binder) { |
| 156 | mService = ((VCardService.MyBinder) binder).getService(); |
| 157 | Log.i(LOG_TAG, |
| 158 | String.format("Connected to VCardService. Kick a vCard cache thread (uri: %s)", |
| 159 | Arrays.toString(mVCardCacheThread.getSourceUris()))); |
| 160 | mVCardCacheThread.start(); |
| 161 | } |
| 162 | |
| 163 | @Override |
| 164 | public void onServiceDisconnected(ComponentName name) { |
| 165 | Log.i(LOG_TAG, "Disconnected from VCardService"); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /** |
| 170 | * Caches given vCard files into a local directory, and sends actual import request to |
| 171 | * {@link VCardService}. |
| 172 | * |
| 173 | * We need to cache given files into local storage. One of reasons is that some data (as Uri) |
| 174 | * may have special permissions. Callers may allow only this Activity to access that content, |
| 175 | * not what this Activity launched (like {@link VCardService}). |
| 176 | */ |
| 177 | private class VCardCacheThread extends Thread |
| 178 | implements DialogInterface.OnCancelListener { |
| 179 | private boolean mCanceled; |
| 180 | private PowerManager.WakeLock mWakeLock; |
| 181 | private VCardParser mVCardParser; |
| 182 | private final Uri[] mSourceUris; // Given from a caller. |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 183 | private final String[] mSourceDisplayNames; // Display names for each Uri in mSourceUris. |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 184 | private final byte[] mSource; |
| 185 | private final String mDisplayName; |
| 186 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 187 | public VCardCacheThread(final Uri[] sourceUris, String[] sourceDisplayNames) { |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 188 | mSourceUris = sourceUris; |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 189 | mSourceDisplayNames = sourceDisplayNames; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 190 | mSource = null; |
| 191 | final Context context = ImportVCardActivity.this; |
| 192 | final PowerManager powerManager = |
| 193 | (PowerManager)context.getSystemService(Context.POWER_SERVICE); |
| 194 | mWakeLock = powerManager.newWakeLock( |
| 195 | PowerManager.SCREEN_DIM_WAKE_LOCK | |
| 196 | PowerManager.ON_AFTER_RELEASE, LOG_TAG); |
| 197 | mDisplayName = null; |
| 198 | } |
| 199 | |
| 200 | @Override |
| 201 | public void finalize() { |
| 202 | if (mWakeLock != null && mWakeLock.isHeld()) { |
| 203 | Log.w(LOG_TAG, "WakeLock is being held."); |
| 204 | mWakeLock.release(); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | @Override |
| 209 | public void run() { |
| 210 | Log.i(LOG_TAG, "vCard cache thread starts running."); |
| 211 | if (mConnection == null) { |
| 212 | throw new NullPointerException("vCard cache thread must be launched " |
| 213 | + "after a service connection is established"); |
| 214 | } |
| 215 | |
| 216 | mWakeLock.acquire(); |
| 217 | try { |
| 218 | if (mCanceled == true) { |
| 219 | Log.i(LOG_TAG, "vCard cache operation is canceled."); |
| 220 | return; |
| 221 | } |
| 222 | |
| 223 | final Context context = ImportVCardActivity.this; |
| 224 | // Uris given from caller applications may not be opened twice: consider when |
| 225 | // it is not from local storage (e.g. "file:///...") but from some special |
| 226 | // provider (e.g. "content://..."). |
| 227 | // Thus we have to once copy the content of Uri into local storage, and read |
| 228 | // it after it. |
| 229 | // |
| 230 | // We may be able to read content of each vCard file during copying them |
| 231 | // to local storage, but currently vCard code does not allow us to do so. |
| 232 | int cache_index = 0; |
| 233 | ArrayList<ImportRequest> requests = new ArrayList<ImportRequest>(); |
| 234 | if (mSource != null) { |
| 235 | try { |
| 236 | requests.add(constructImportRequest(mSource, null, mDisplayName)); |
| 237 | } catch (VCardException e) { |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 238 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 239 | "Failed to cache vcard", e); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 240 | showFailureNotification(R.string.fail_reason_not_supported); |
| 241 | return; |
| 242 | } |
| 243 | } else { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 244 | int i = 0; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 245 | for (Uri sourceUri : mSourceUris) { |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 246 | if (mCanceled) { |
| 247 | Log.i(LOG_TAG, "vCard cache operation is canceled."); |
| 248 | break; |
| 249 | } |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 250 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 251 | String sourceDisplayName = mSourceDisplayNames[i++]; |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 252 | |
| 253 | final ImportRequest request; |
| 254 | try { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 255 | request = constructImportRequest(null, sourceUri, sourceDisplayName); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 256 | } catch (VCardException e) { |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 257 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 258 | "Failed to cache vcard", e); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 259 | showFailureNotification(R.string.fail_reason_not_supported); |
| 260 | return; |
| 261 | } catch (IOException e) { |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 262 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 263 | "Failed to cache vcard", e); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 264 | showFailureNotification(R.string.fail_reason_io_error); |
| 265 | return; |
| 266 | } |
| 267 | if (mCanceled) { |
| 268 | Log.i(LOG_TAG, "vCard cache operation is canceled."); |
| 269 | return; |
| 270 | } |
| 271 | requests.add(request); |
| 272 | } |
| 273 | } |
| 274 | if (!requests.isEmpty()) { |
| 275 | mConnection.sendImportRequest(requests); |
| 276 | } else { |
| 277 | Log.w(LOG_TAG, "Empty import requests. Ignore it."); |
| 278 | } |
| 279 | } catch (OutOfMemoryError e) { |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 280 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 281 | "OutOfMemoryError occured during caching vCard", e); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 282 | System.gc(); |
| 283 | runOnUiThread(new DialogDisplayer( |
| 284 | getString(R.string.fail_reason_low_memory_during_import))); |
| 285 | } catch (IOException e) { |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 286 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 287 | "IOException during caching vCard", e); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 288 | runOnUiThread(new DialogDisplayer( |
| 289 | getString(R.string.fail_reason_io_error))); |
| 290 | } finally { |
| 291 | Log.i(LOG_TAG, "Finished caching vCard."); |
| 292 | mWakeLock.release(); |
yaolu | 455d082 | 2016-11-16 14:56:11 -0800 | [diff] [blame] | 293 | try { |
| 294 | unbindService(mConnection); |
| 295 | } catch (IllegalArgumentException e) { |
| 296 | FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG, |
| 297 | "Cannot unbind service connection", e); |
| 298 | } |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 299 | mProgressDialogForCachingVCard.dismiss(); |
| 300 | mProgressDialogForCachingVCard = null; |
| 301 | finish(); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | /** |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 306 | * Reads localDataUri (possibly multiple times) and constructs {@link ImportRequest} from |
| 307 | * its content. |
| 308 | * |
| 309 | * @arg localDataUri Uri actually used for the import. Should be stored in |
| 310 | * app local storage, as we cannot guarantee other types of Uris can be read |
| 311 | * multiple times. This variable populates {@link ImportRequest#uri}. |
| 312 | * @arg displayName Used for displaying information to the user. This variable populates |
| 313 | * {@link ImportRequest#displayName}. |
| 314 | */ |
| 315 | private ImportRequest constructImportRequest(final byte[] data, |
| 316 | final Uri localDataUri, final String displayName) |
| 317 | throws IOException, VCardException { |
| 318 | final ContentResolver resolver = ImportVCardActivity.this.getContentResolver(); |
| 319 | VCardEntryCounter counter = null; |
| 320 | VCardSourceDetector detector = null; |
| 321 | int vcardVersion = VCARD_VERSION_V21; |
| 322 | try { |
| 323 | boolean shouldUseV30 = false; |
| 324 | InputStream is; |
| 325 | if (data != null) { |
| 326 | is = new ByteArrayInputStream(data); |
| 327 | } else { |
| 328 | is = resolver.openInputStream(localDataUri); |
| 329 | } |
| 330 | mVCardParser = new VCardParser_V21(); |
| 331 | try { |
| 332 | counter = new VCardEntryCounter(); |
| 333 | detector = new VCardSourceDetector(); |
| 334 | mVCardParser.addInterpreter(counter); |
| 335 | mVCardParser.addInterpreter(detector); |
| 336 | mVCardParser.parse(is); |
| 337 | } catch (VCardVersionException e1) { |
| 338 | try { |
| 339 | is.close(); |
| 340 | } catch (IOException e) { |
| 341 | } |
| 342 | |
| 343 | shouldUseV30 = true; |
| 344 | if (data != null) { |
| 345 | is = new ByteArrayInputStream(data); |
| 346 | } else { |
| 347 | is = resolver.openInputStream(localDataUri); |
| 348 | } |
| 349 | mVCardParser = new VCardParser_V30(); |
| 350 | try { |
| 351 | counter = new VCardEntryCounter(); |
| 352 | detector = new VCardSourceDetector(); |
| 353 | mVCardParser.addInterpreter(counter); |
| 354 | mVCardParser.addInterpreter(detector); |
| 355 | mVCardParser.parse(is); |
| 356 | } catch (VCardVersionException e2) { |
| 357 | throw new VCardException("vCard with unspported version."); |
| 358 | } |
| 359 | } finally { |
| 360 | if (is != null) { |
| 361 | try { |
| 362 | is.close(); |
| 363 | } catch (IOException e) { |
| 364 | } |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | vcardVersion = shouldUseV30 ? VCARD_VERSION_V30 : VCARD_VERSION_V21; |
| 369 | } catch (VCardNestedException e) { |
| 370 | Log.w(LOG_TAG, "Nested Exception is found (it may be false-positive)."); |
| 371 | // Go through without throwing the Exception, as we may be able to detect the |
| 372 | // version before it |
| 373 | } |
| 374 | return new ImportRequest(mAccount, |
| 375 | data, localDataUri, displayName, |
| 376 | detector.getEstimatedType(), |
| 377 | detector.getEstimatedCharset(), |
| 378 | vcardVersion, counter.getCount()); |
| 379 | } |
| 380 | |
| 381 | public Uri[] getSourceUris() { |
| 382 | return mSourceUris; |
| 383 | } |
| 384 | |
| 385 | public void cancel() { |
| 386 | mCanceled = true; |
| 387 | if (mVCardParser != null) { |
| 388 | mVCardParser.cancel(); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | @Override |
| 393 | public void onCancel(DialogInterface dialog) { |
| 394 | Log.i(LOG_TAG, "Cancel request has come. Abort caching vCard."); |
| 395 | cancel(); |
| 396 | } |
| 397 | } |
| 398 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 399 | private void importVCard(final Uri uri, final String sourceDisplayName) { |
| 400 | importVCard(new Uri[] {uri}, new String[] {sourceDisplayName}); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 401 | } |
| 402 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 403 | private void importVCard(final Uri[] uris, final String[] sourceDisplayNames) { |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 404 | runOnUiThread(new Runnable() { |
| 405 | @Override |
| 406 | public void run() { |
| 407 | if (!isFinishing()) { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 408 | mVCardCacheThread = new VCardCacheThread(uris, sourceDisplayNames); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 409 | mListener = new NotificationImportExportListener(ImportVCardActivity.this); |
| 410 | showDialog(R.id.dialog_cache_vcard); |
| 411 | } |
| 412 | } |
| 413 | }); |
| 414 | } |
| 415 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 416 | private String getDisplayName(Uri sourceUri) { |
Tingting Wang | 0d53352 | 2015-12-14 11:53:01 -0800 | [diff] [blame] | 417 | if (sourceUri == null) { |
| 418 | return null; |
| 419 | } |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 420 | final ContentResolver resolver = ImportVCardActivity.this.getContentResolver(); |
| 421 | String displayName = null; |
| 422 | Cursor cursor = null; |
| 423 | // Try to get a display name from the given Uri. If it fails, we just |
| 424 | // pick up the last part of the Uri. |
| 425 | try { |
| 426 | cursor = resolver.query(sourceUri, |
| 427 | new String[] { OpenableColumns.DISPLAY_NAME }, |
| 428 | null, null, null); |
| 429 | if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { |
| 430 | if (cursor.getCount() > 1) { |
| 431 | Log.w(LOG_TAG, "Unexpected multiple rows: " |
| 432 | + cursor.getCount()); |
| 433 | } |
| 434 | int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); |
| 435 | if (index >= 0) { |
| 436 | displayName = cursor.getString(index); |
| 437 | } |
| 438 | } |
| 439 | } finally { |
| 440 | if (cursor != null) { |
| 441 | cursor.close(); |
| 442 | } |
| 443 | } |
| 444 | if (TextUtils.isEmpty(displayName)){ |
| 445 | displayName = sourceUri.getLastPathSegment(); |
| 446 | } |
| 447 | return displayName; |
| 448 | } |
| 449 | |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 450 | /** |
| 451 | * Copy the content of sourceUri to the destination. |
| 452 | */ |
| 453 | private Uri copyTo(final Uri sourceUri, String filename) throws IOException { |
| 454 | Log.i(LOG_TAG, String.format("Copy a Uri to app local storage (%s -> %s)", |
| 455 | sourceUri, filename)); |
| 456 | final Context context = ImportVCardActivity.this; |
| 457 | final ContentResolver resolver = context.getContentResolver(); |
| 458 | ReadableByteChannel inputChannel = null; |
| 459 | WritableByteChannel outputChannel = null; |
| 460 | Uri destUri = null; |
| 461 | try { |
| 462 | inputChannel = Channels.newChannel(resolver.openInputStream(sourceUri)); |
| 463 | destUri = Uri.parse(context.getFileStreamPath(filename).toURI().toString()); |
| 464 | outputChannel = context.openFileOutput(filename, Context.MODE_PRIVATE).getChannel(); |
| 465 | final ByteBuffer buffer = ByteBuffer.allocateDirect(8192); |
| 466 | while (inputChannel.read(buffer) != -1) { |
| 467 | buffer.flip(); |
| 468 | outputChannel.write(buffer); |
| 469 | buffer.compact(); |
| 470 | } |
| 471 | buffer.flip(); |
| 472 | while (buffer.hasRemaining()) { |
| 473 | outputChannel.write(buffer); |
| 474 | } |
| 475 | } finally { |
| 476 | if (inputChannel != null) { |
| 477 | try { |
| 478 | inputChannel.close(); |
| 479 | } catch (IOException e) { |
| 480 | Log.w(LOG_TAG, "Failed to close inputChannel."); |
| 481 | } |
| 482 | } |
| 483 | if (outputChannel != null) { |
| 484 | try { |
| 485 | outputChannel.close(); |
| 486 | } catch(IOException e) { |
| 487 | Log.w(LOG_TAG, "Failed to close outputChannel"); |
| 488 | } |
| 489 | } |
| 490 | } |
| 491 | return destUri; |
| 492 | } |
| 493 | |
| 494 | /** |
| 495 | * Reads the file from {@param sourceUri} and copies it to local cache file. |
| 496 | * Returns the local file name which stores the file from sourceUri. |
| 497 | */ |
| 498 | private String readUriToLocalFile(Uri sourceUri) { |
| 499 | // Read the uri to local first. |
| 500 | int cache_index = 0; |
| 501 | String localFilename = null; |
| 502 | // Note: caches are removed by VCardService. |
| 503 | while (true) { |
| 504 | localFilename = VCardService.CACHE_FILE_PREFIX + cache_index + ".vcf"; |
| 505 | final File file = getFileStreamPath(localFilename); |
| 506 | if (!file.exists()) { |
| 507 | break; |
| 508 | } else { |
| 509 | if (cache_index == Integer.MAX_VALUE) { |
| 510 | throw new RuntimeException("Exceeded cache limit"); |
| 511 | } |
| 512 | cache_index++; |
| 513 | } |
| 514 | } |
| 515 | try { |
| 516 | copyTo(sourceUri, localFilename); |
Walter Jang | 3a0b483 | 2016-10-12 11:02:54 -0700 | [diff] [blame] | 517 | } catch (IOException|SecurityException e) { |
| 518 | FeedbackHelper.sendFeedback(this, LOG_TAG, "Failed to copy vcard to local file", e); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 519 | showFailureNotification(R.string.fail_reason_io_error); |
| 520 | return null; |
| 521 | } |
| 522 | |
| 523 | if (localFilename == null) { |
| 524 | Log.e(LOG_TAG, "Cannot load uri to local storage."); |
| 525 | showFailureNotification(R.string.fail_reason_io_error); |
| 526 | return null; |
| 527 | } |
| 528 | |
| 529 | return localFilename; |
| 530 | } |
| 531 | |
| 532 | private Uri readUriToLocalUri(Uri sourceUri) { |
| 533 | final String fileName = readUriToLocalFile(sourceUri); |
Jay Shrauner | 6890945 | 2015-12-14 11:09:31 -0800 | [diff] [blame] | 534 | if (fileName == null) { |
| 535 | return null; |
| 536 | } |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 537 | return Uri.parse(getFileStreamPath(fileName).toURI().toString()); |
| 538 | } |
| 539 | |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 540 | // Returns true if uri is from Storage. |
| 541 | private boolean isStorageUri(Uri uri) { |
| 542 | return uri != null && uri.toString().startsWith(STORAGE_VCARD_URI_PREFIX); |
Tingting Wang | a6139f9 | 2015-11-30 12:44:27 -0800 | [diff] [blame] | 543 | } |
| 544 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 545 | @Override |
| 546 | protected void onCreate(Bundle bundle) { |
| 547 | super.onCreate(bundle); |
| 548 | |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 549 | Uri sourceUri = getIntent().getData(); |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 550 | |
| 551 | // Reading uris from non-storage needs the permission granted from the source intent, |
Tingting Wang | a6139f9 | 2015-11-30 12:44:27 -0800 | [diff] [blame] | 552 | // instead of permissions from RequestImportVCardPermissionActivity. So skipping requesting |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 553 | // permissions from RequestImportVCardPermissionActivity for uris from non-storage source. |
Walter Jang | 915ccdd | 2016-10-24 12:18:20 -0700 | [diff] [blame] | 554 | if (isStorageUri(sourceUri) && RequestImportVCardPermissionsActivity |
| 555 | .startPermissionActivity(this, isCallerSelf(this))) { |
Tingting Wang | 5da0bdf | 2015-11-17 21:53:13 -0800 | [diff] [blame] | 556 | return; |
| 557 | } |
| 558 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 559 | String sourceDisplayName = null; |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 560 | if (sourceUri != null) { |
| 561 | // Read the uri to local first. |
| 562 | String localTmpFileName = getIntent().getStringExtra(LOCAL_TMP_FILE_NAME_EXTRA); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 563 | sourceDisplayName = getIntent().getStringExtra(SOURCE_URI_DISPLAY_NAME); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 564 | if (TextUtils.isEmpty(localTmpFileName)) { |
| 565 | localTmpFileName = readUriToLocalFile(sourceUri); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 566 | sourceDisplayName = getDisplayName(sourceUri); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 567 | if (localTmpFileName == null) { |
| 568 | Log.e(LOG_TAG, "Cannot load uri to local storage."); |
| 569 | showFailureNotification(R.string.fail_reason_io_error); |
| 570 | return; |
| 571 | } |
| 572 | getIntent().putExtra(LOCAL_TMP_FILE_NAME_EXTRA, localTmpFileName); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 573 | getIntent().putExtra(SOURCE_URI_DISPLAY_NAME, sourceDisplayName); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 574 | } |
| 575 | sourceUri = Uri.parse(getFileStreamPath(localTmpFileName).toURI().toString()); |
| 576 | } |
| 577 | |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 578 | // Always request required permission for contacts before importing the vcard. |
Walter Jang | 915ccdd | 2016-10-24 12:18:20 -0700 | [diff] [blame] | 579 | if (RequestImportVCardPermissionsActivity.startPermissionActivity(this, |
| 580 | isCallerSelf(this))) { |
Tingting Wang | 683d770 | 2016-02-04 15:54:50 -0800 | [diff] [blame] | 581 | return; |
| 582 | } |
| 583 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 584 | String accountName = null; |
| 585 | String accountType = null; |
| 586 | String dataSet = null; |
| 587 | final Intent intent = getIntent(); |
| 588 | if (intent != null) { |
| 589 | accountName = intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME); |
| 590 | accountType = intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE); |
| 591 | dataSet = intent.getStringExtra(SelectAccountActivity.DATA_SET); |
| 592 | } else { |
| 593 | Log.e(LOG_TAG, "intent does not exist"); |
| 594 | } |
| 595 | |
| 596 | if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { |
| 597 | mAccount = new AccountWithDataSet(accountName, accountType, dataSet); |
| 598 | } else { |
| 599 | final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this); |
| 600 | final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true); |
| 601 | if (accountList.size() == 0) { |
| 602 | mAccount = null; |
| 603 | } else if (accountList.size() == 1) { |
| 604 | mAccount = accountList.get(0); |
| 605 | } else { |
| 606 | startActivityForResult(new Intent(this, SelectAccountActivity.class), |
| 607 | SELECT_ACCOUNT); |
| 608 | return; |
| 609 | } |
| 610 | } |
| 611 | |
Walter Jang | 915ccdd | 2016-10-24 12:18:20 -0700 | [diff] [blame] | 612 | if (isCallerSelf(this)) { |
| 613 | startImport(sourceUri, sourceDisplayName); |
| 614 | } else { |
| 615 | ImportVCardDialogFragment.show(this, sourceUri, sourceDisplayName); |
| 616 | } |
| 617 | } |
| 618 | |
| 619 | private static boolean isCallerSelf(Activity activity) { |
| 620 | // {@link Activity#getCallingActivity()} is a safer alternative to |
| 621 | // {@link Activity#getCallingPackage()} that works around a |
| 622 | // framework bug where getCallingPackage() can sometimes return null even when the |
| 623 | // current activity *was* in fact launched via a startActivityForResult() call. |
| 624 | // |
| 625 | // (The bug happens if the task stack needs to be re-created by the framework after |
| 626 | // having been killed due to memory pressure or by the "Don't keep activities" |
| 627 | // developer option; see bug 7494866 for the full details.) |
| 628 | // |
| 629 | // Turns out that {@link Activity#getCallingActivity()} *does* return correct info |
| 630 | // even in the case where getCallingPackage() is broken, so the workaround is simply |
| 631 | // to get the package name from getCallingActivity().getPackageName() instead. |
| 632 | final ComponentName callingActivity = activity.getCallingActivity(); |
| 633 | if (callingActivity == null) return false; |
| 634 | final String packageName = callingActivity.getPackageName(); |
| 635 | if (packageName == null) return false; |
| 636 | return packageName.equals(activity.getApplicationContext().getPackageName()); |
| 637 | } |
| 638 | |
| 639 | @Override |
| 640 | public void onImportVCardConfirmed(Uri sourceUri, String sourceDisplayName) { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 641 | startImport(sourceUri, sourceDisplayName); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 642 | } |
| 643 | |
| 644 | @Override |
Walter Jang | 915ccdd | 2016-10-24 12:18:20 -0700 | [diff] [blame] | 645 | public void onImportVCardDenied() { |
| 646 | finish(); |
| 647 | } |
| 648 | |
| 649 | @Override |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 650 | public void onActivityResult(int requestCode, int resultCode, Intent intent) { |
| 651 | if (requestCode == SELECT_ACCOUNT) { |
| 652 | if (resultCode == Activity.RESULT_OK) { |
| 653 | mAccount = new AccountWithDataSet( |
| 654 | intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME), |
| 655 | intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE), |
| 656 | intent.getStringExtra(SelectAccountActivity.DATA_SET)); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 657 | final Uri sourceUri = getIntent().getData(); |
| 658 | if (sourceUri == null) { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 659 | startImport(sourceUri, /* sourceDisplayName =*/ null); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 660 | } else { |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 661 | final String sourceDisplayName = getIntent().getStringExtra( |
| 662 | SOURCE_URI_DISPLAY_NAME); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 663 | final String localFileName = getIntent().getStringExtra( |
| 664 | LOCAL_TMP_FILE_NAME_EXTRA); |
| 665 | final Uri localUri = Uri.parse( |
| 666 | getFileStreamPath(localFileName).toURI().toString()); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 667 | startImport(localUri, sourceDisplayName); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 668 | } |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 669 | } else { |
| 670 | if (resultCode != Activity.RESULT_CANCELED) { |
| 671 | Log.w(LOG_TAG, "Result code was not OK nor CANCELED: " + resultCode); |
| 672 | } |
| 673 | finish(); |
| 674 | } |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 675 | } else if (requestCode == REQUEST_OPEN_DOCUMENT) { |
| 676 | if (resultCode == Activity.RESULT_OK) { |
| 677 | final ClipData clipData = intent.getClipData(); |
| 678 | if (clipData != null) { |
| 679 | final ArrayList<Uri> uris = new ArrayList<>(); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 680 | final ArrayList<String> sourceDisplayNames = new ArrayList<>(); |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 681 | for (int i = 0; i < clipData.getItemCount(); i++) { |
| 682 | ClipData.Item item = clipData.getItemAt(i); |
| 683 | final Uri uri = item.getUri(); |
| 684 | if (uri != null) { |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 685 | final Uri localUri = readUriToLocalUri(uri); |
Jay Shrauner | 6890945 | 2015-12-14 11:09:31 -0800 | [diff] [blame] | 686 | if (localUri != null) { |
| 687 | final String sourceDisplayName = getDisplayName(uri); |
| 688 | uris.add(localUri); |
| 689 | sourceDisplayNames.add(sourceDisplayName); |
| 690 | } |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 691 | } |
| 692 | } |
| 693 | if (uris.isEmpty()) { |
| 694 | Log.w(LOG_TAG, "No vCard was selected for import"); |
| 695 | finish(); |
| 696 | } else { |
| 697 | Log.i(LOG_TAG, "Multiple vCards selected for import: " + uris); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 698 | importVCard(uris.toArray(new Uri[0]), |
| 699 | sourceDisplayNames.toArray(new String[0])); |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 700 | } |
| 701 | } else { |
| 702 | final Uri uri = intent.getData(); |
| 703 | if (uri != null) { |
| 704 | Log.i(LOG_TAG, "vCard selected for import: " + uri); |
Tingting Wang | 1fdb890 | 2015-09-08 16:34:09 -0700 | [diff] [blame] | 705 | final Uri localUri = readUriToLocalUri(uri); |
Jay Shrauner | 6890945 | 2015-12-14 11:09:31 -0800 | [diff] [blame] | 706 | if (localUri != null) { |
Tingting Wang | 0d53352 | 2015-12-14 11:53:01 -0800 | [diff] [blame] | 707 | final String sourceDisplayName = getDisplayName(uri); |
Jay Shrauner | 6890945 | 2015-12-14 11:09:31 -0800 | [diff] [blame] | 708 | importVCard(localUri, sourceDisplayName); |
| 709 | } else { |
| 710 | Log.w(LOG_TAG, "No local URI for vCard import"); |
| 711 | finish(); |
| 712 | } |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 713 | } else { |
| 714 | Log.w(LOG_TAG, "No vCard was selected for import"); |
| 715 | finish(); |
| 716 | } |
| 717 | } |
| 718 | } else { |
| 719 | if (resultCode != Activity.RESULT_CANCELED) { |
| 720 | Log.w(LOG_TAG, "Result code was not OK nor CANCELED" + resultCode); |
| 721 | } |
| 722 | finish(); |
| 723 | } |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 724 | } |
| 725 | } |
| 726 | |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 727 | private void startImport(Uri uri, String sourceDisplayName) { |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 728 | // Handle inbound files |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 729 | if (uri != null) { |
| 730 | Log.i(LOG_TAG, "Starting vCard import using Uri " + uri); |
Tingting Wang | 7e0d93c | 2015-09-16 10:47:57 -0700 | [diff] [blame] | 731 | importVCard(uri, sourceDisplayName); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 732 | } else { |
| 733 | Log.i(LOG_TAG, "Start vCard without Uri. The user will select vCard manually."); |
Walter Jang | 5443426 | 2015-08-11 09:18:35 -0700 | [diff] [blame] | 734 | final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 735 | intent.addCategory(Intent.CATEGORY_OPENABLE); |
Walter Jang | 5443426 | 2015-08-11 09:18:35 -0700 | [diff] [blame] | 736 | intent.setType(VCardService.X_VCARD_MIME_TYPE); |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 737 | intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); |
Walter Jang | 6321e5a | 2015-07-14 10:56:03 -0700 | [diff] [blame] | 738 | startActivityForResult(intent, REQUEST_OPEN_DOCUMENT); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 739 | } |
| 740 | } |
| 741 | |
| 742 | @Override |
| 743 | protected Dialog onCreateDialog(int resId, Bundle bundle) { |
Sailesh Nepal | b4a522e | 2016-02-20 21:17:32 -0800 | [diff] [blame] | 744 | if (resId == R.id.dialog_cache_vcard) { |
| 745 | if (mProgressDialogForCachingVCard == null) { |
| 746 | final String title = getString(R.string.caching_vcard_title); |
| 747 | final String message = getString(R.string.caching_vcard_message); |
| 748 | mProgressDialogForCachingVCard = new ProgressDialog(this); |
| 749 | mProgressDialogForCachingVCard.setTitle(title); |
| 750 | mProgressDialogForCachingVCard.setMessage(message); |
| 751 | mProgressDialogForCachingVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER); |
| 752 | mProgressDialogForCachingVCard.setOnCancelListener(mVCardCacheThread); |
| 753 | startVCardService(); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 754 | } |
Sailesh Nepal | b4a522e | 2016-02-20 21:17:32 -0800 | [diff] [blame] | 755 | return mProgressDialogForCachingVCard; |
| 756 | } else if (resId == R.id.dialog_error_with_message) { |
| 757 | String message = mErrorMessage; |
| 758 | if (TextUtils.isEmpty(message)) { |
| 759 | Log.e(LOG_TAG, "Error message is null while it must not."); |
| 760 | message = getString(R.string.fail_reason_unknown); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 761 | } |
Sailesh Nepal | b4a522e | 2016-02-20 21:17:32 -0800 | [diff] [blame] | 762 | final AlertDialog.Builder builder = new AlertDialog.Builder(this) |
| 763 | .setTitle(getString(R.string.reading_vcard_failed_title)) |
| 764 | .setIconAttribute(android.R.attr.alertDialogIcon) |
| 765 | .setMessage(message) |
| 766 | .setOnCancelListener(mCancelListener) |
| 767 | .setPositiveButton(android.R.string.ok, mCancelListener); |
| 768 | return builder.create(); |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 769 | } |
| 770 | |
| 771 | return super.onCreateDialog(resId, bundle); |
| 772 | } |
| 773 | |
| 774 | /* package */ void startVCardService() { |
| 775 | mConnection = new ImportRequestConnection(); |
| 776 | |
| 777 | Log.i(LOG_TAG, "Bind to VCardService."); |
| 778 | // We don't want the service finishes itself just after this connection. |
| 779 | Intent intent = new Intent(this, VCardService.class); |
| 780 | startService(intent); |
| 781 | bindService(new Intent(this, VCardService.class), |
| 782 | mConnection, Context.BIND_AUTO_CREATE); |
| 783 | } |
| 784 | |
| 785 | @Override |
| 786 | protected void onRestoreInstanceState(Bundle savedInstanceState) { |
| 787 | super.onRestoreInstanceState(savedInstanceState); |
| 788 | if (mProgressDialogForCachingVCard != null) { |
| 789 | Log.i(LOG_TAG, "Cache thread is still running. Show progress dialog again."); |
| 790 | showDialog(R.id.dialog_cache_vcard); |
| 791 | } |
| 792 | } |
| 793 | |
Chiao Cheng | d80c434 | 2012-12-03 17:15:58 -0800 | [diff] [blame] | 794 | /* package */ void showFailureNotification(int reasonId) { |
| 795 | final NotificationManager notificationManager = |
| 796 | (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); |
| 797 | final Notification notification = |
| 798 | NotificationImportExportListener.constructImportFailureNotification( |
| 799 | ImportVCardActivity.this, |
| 800 | getString(reasonId)); |
| 801 | notificationManager.notify(NotificationImportExportListener.FAILURE_NOTIFICATION_TAG, |
| 802 | FAILURE_NOTIFICATION_ID, notification); |
| 803 | mHandler.post(new Runnable() { |
| 804 | @Override |
| 805 | public void run() { |
| 806 | Toast.makeText(ImportVCardActivity.this, |
| 807 | getString(R.string.vcard_import_failed), Toast.LENGTH_LONG).show(); |
| 808 | } |
| 809 | }); |
| 810 | } |
| 811 | } |