diff options
author | Walter Jang <wjang@google.com> | 2016-10-25 15:42:33 -0700 |
---|---|---|
committer | xpduyson <xpduyson@gmail.com> | 2017-03-09 20:45:44 +0700 |
commit | 11ea676f79b93051eca90eb164e9fb019b4b8885 (patch) | |
tree | 7f91b207dd2e1d91080701ae2ebb844ab349418f | |
parent | 7e794a662b429c82bfdfbcc61ee0cd1c9d74cf7a (diff) | |
download | packages_apps_ContactsCommon-11ea676f79b93051eca90eb164e9fb019b4b8885.tar.gz packages_apps_ContactsCommon-11ea676f79b93051eca90eb164e9fb019b4b8885.tar.bz2 packages_apps_ContactsCommon-11ea676f79b93051eca90eb164e9fb019b4b8885.zip |
Ask for confirmation before importing from vcardcm-11.0
But only when vcard import is started from outside
the contacts application itself.
Test: Manually confirmed the following behavior
1. When there is 0 or 1 writable contacts providing
account on the device:
- Importing contacts from the contacts app itself
DOES NOT SHOW the new confirmation dialog.
- Sending a vcard to contacts from another application
DOES SHOW the new confirmation dialog.
2. When there are 2 or more writable contacts providing
accounts on the device:
- Importing contacts from the contacts app itself
DOES NOT SHOW the new confirmation dialog, instead the
existing account picker dialog is displayed.
- Sending a vcard to contacts from another application
DOES NOT SHOW the new confirmation dialog, instead the
existing account picker dialog is displayed.
Bug: 32219099
Change-Id: Ia57ae539112e752fe4fd1f01407b7905f1bc02fa
(cherry picked from commit 0b6c338eb238a4ca061372874e5213205d5ea1b1)
4 files changed, 139 insertions, 40 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index c63fb33d..afc2fd94 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -420,6 +420,9 @@ a ren't members of any other group. [CHAR LIMIT=25] --> <!-- Action string for selecting (USB) storage for importing contacts [CHAR LIMIT=30] --> <string name="import_from_sdcard" product="default">Import from storage</string> + + <!-- Dialog message asking the user for confirmation before starting to import contacts from a .vcf file. [CHAR LIMIT=NONE] --> + <string name="import_from_vcf_file_confirmation_message" product="default">Import contacts from vCard?</string> <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard import. The argument is file name for the vCard import the user wants to cancel. diff --git a/src/com/android/contacts/common/util/AccountSelectionUtil.java b/src/com/android/contacts/common/util/AccountSelectionUtil.java index cfe763d9..d9a7fc7d 100644 --- a/src/com/android/contacts/common/util/AccountSelectionUtil.java +++ b/src/com/android/contacts/common/util/AccountSelectionUtil.java @@ -18,6 +18,7 @@ package com.android.contacts.common.util; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; @@ -68,24 +69,24 @@ public class AccountSelectionUtil { public static class AccountSelectedListener implements DialogInterface.OnClickListener { - final private Context mContext; + final private Activity mActivity; final private int mResId; protected List<AccountWithDataSet> mAccountList; - public AccountSelectedListener(Context context, List<AccountWithDataSet> accountList, + public AccountSelectedListener(Activity activity, List<AccountWithDataSet> accountList, int resId) { if (accountList == null || accountList.size() == 0) { Log.e(LOG_TAG, "The size of Account list is 0."); } - mContext = context; + mActivity = activity; mAccountList = accountList; mResId = resId; } public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); - doImport(mContext, mResId, mAccountList.get(which)); + doImport(mActivity, mResId, mAccountList.get(which)); } /** * Reset the account list for this listener, to make sure the selected @@ -102,16 +103,16 @@ public class AccountSelectionUtil { mImportSub = subscription; } - public static Dialog getSelectAccountDialog(Context context, int resId) { - return getSelectAccountDialog(context, resId, null, null); - } + public static Dialog getSelectAccountDialog(Activity activity, int resId) { + return getSelectAccountDialog(activity, resId, null, null); + } - public static Dialog getSelectAccountDialog(Context context, int resId, + public static Dialog getSelectAccountDialog(Activity activity, int resId, DialogInterface.OnClickListener onClickListener) { - return getSelectAccountDialog(context, resId, onClickListener, null); + return getSelectAccountDialog(activity, resId, onClickListener, null); } - public static Dialog getSelectAccountDialog(Context context, int resId, + public static Dialog getSelectAccountDialog(Activity context, int resId, DialogInterface.OnClickListener onClickListener, DialogInterface.OnCancelListener onCancelListener) { return getSelectAccountDialog(context, resId, onClickListener, @@ -122,10 +123,10 @@ public class AccountSelectionUtil { * When OnClickListener or OnCancelListener is null, uses a default listener. * The default OnCancelListener just closes itself with {@link Dialog#dismiss()}. */ - public static Dialog getSelectAccountDialog(Context context, int resId, + public static Dialog getSelectAccountDialog(Activity activity, int resId, DialogInterface.OnClickListener onClickListener, DialogInterface.OnCancelListener onCancelListener, boolean includeSIM) { - final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context); + final AccountTypeManager accountTypes = AccountTypeManager.getInstance(activity); List<AccountWithDataSet> writableAccountList = accountTypes.getAccounts(true); if (includeSIM) { writableAccountList = accountTypes.getAccounts(true); @@ -140,11 +141,11 @@ public class AccountSelectionUtil { // Wrap our context to inflate list items using correct theme final Context dialogContext = new ContextThemeWrapper( - context, android.R.style.Theme_Light); + activity, android.R.style.Theme_Light); final LayoutInflater dialogInflater = (LayoutInflater)dialogContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); final ArrayAdapter<AccountWithDataSet> accountAdapter = - new ArrayAdapter<AccountWithDataSet>(context, android.R.layout.simple_list_item_2, + new ArrayAdapter<AccountWithDataSet>(activity, android.R.layout.simple_list_item_2, writableAccountList) { @Override @@ -175,7 +176,7 @@ public class AccountSelectionUtil { if (onClickListener == null) { AccountSelectedListener accountSelectedListener = - new AccountSelectedListener(context, writableAccountList, resId); + new AccountSelectedListener(activity, writableAccountList, resId); onClickListener = accountSelectedListener; } else if (onClickListener instanceof AccountSelectedListener) { // Because the writableAccountList is different if includeSIM or not, so @@ -192,21 +193,21 @@ public class AccountSelectionUtil { } }; } - return new AlertDialog.Builder(context) + return new AlertDialog.Builder(activity) .setTitle(R.string.dialog_new_contact_account) .setSingleChoiceItems(accountAdapter, 0, onClickListener) .setOnCancelListener(onCancelListener) .create(); } - public static void doImport(Context context, int resId, AccountWithDataSet account) { + public static void doImport(Activity activity, int resId, AccountWithDataSet account) { switch (resId) { case R.string.import_from_sim: { - doImportFromSim(context, account); + doImportFromSim(activity, account); break; } case R.string.import_from_sdcard: { - doImportFromSdCard(context, account); + doImportFromSdCard(activity, account); break; } } @@ -241,8 +242,8 @@ public class AccountSelectionUtil { context.startActivity(importIntent); } - public static void doImportFromSdCard(Context context, AccountWithDataSet account) { - Intent importIntent = new Intent(context, ImportVCardActivity.class); + public static void doImportFromSdCard(Activity activity, AccountWithDataSet account) { + Intent importIntent = new Intent(activity, ImportVCardActivity.class); if (account != null) { importIntent.putExtra(SimContactsConstants.ACCOUNT_NAME, account.name); importIntent.putExtra(SimContactsConstants.ACCOUNT_TYPE, account.type); @@ -260,7 +261,7 @@ public class AccountSelectionUtil { } mVCardShare = false; mPath = null; - context.startActivity(importIntent); + activity.startActivityForResult(importIntent, 0); } public static class SimSelectedListener diff --git a/src/com/android/contacts/common/vcard/ImportVCardActivity.java b/src/com/android/contacts/common/vcard/ImportVCardActivity.java index c15d5df0..3439e1f7 100644 --- a/src/com/android/contacts/common/vcard/ImportVCardActivity.java +++ b/src/com/android/contacts/common/vcard/ImportVCardActivity.java @@ -92,8 +92,10 @@ import java.util.regex.Pattern; * any Dialog in the instance. So this code is careless about the management around managed * dialogs stuffs (like how onCreateDialog() is used). */ -public class ImportVCardActivity extends Activity implements SelectAccountDialogFragment.Listener { +public class ImportVCardActivity extends Activity implements ImportVCardDialogFragment.Listener { private static final String LOG_TAG = "VCardImport"; + + private static final int SELECT_ACCOUNT = 0; /* package */ static final String VCARD_URI_ARRAY = "vcard_uri"; /* package */ static final String ESTIMATED_VCARD_TYPE_ARRAY = "estimated_vcard_type"; @@ -141,17 +143,6 @@ public class ImportVCardActivity extends Activity implements SelectAccountDialog private Handler mHandler = new Handler(); - @Override - public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) { - mAccount = account; - startImport(); - } - - @Override - public void onAccountSelectorCancelled() { - finish(); - } - private static class VCardFile { private final String mName; private final String mCanonicalPath; @@ -900,17 +891,45 @@ public class ImportVCardActivity extends Activity implements SelectAccountDialog } else if (accountList.size() == 1) { mAccount = accountList.get(0); } else { - SelectAccountDialogFragment.show( - getFragmentManager(), this, - R.string.dialog_new_contact_account, - AccountsListAdapter.AccountListFilter.ACCOUNTS_CONTACT_WRITABLE_WITHOUT_SIM, - null); + startActivityForResult(new Intent(this, SelectAccountActivity.class), + SELECT_ACCOUNT); return; } } - + if (isCallerSelf(this)) { + startImport(); + } else { + ImportVCardDialogFragment.show(this); + } + } + private static boolean isCallerSelf(Activity activity) { + // {@link Activity#getCallingActivity()} is a safer alternative to + // {@link Activity#getCallingPackage()} that works around a + // framework bug where getCallingPackage() can sometimes return null even when the + // current activity *was* in fact launched via a startActivityForResult() call. + // + // (The bug happens if the task stack needs to be re-created by the framework after + // having been killed due to memory pressure or by the "Don't keep activities" + // developer option; see bug 7494866 for the full details.) + // + // Turns out that {@link Activity#getCallingActivity()} *does* return correct info + // even in the case where getCallingPackage() is broken, so the workaround is simply + // to get the package name from getCallingActivity().getPackageName() instead. + final ComponentName callingActivity = activity.getCallingActivity(); + if (callingActivity == null) return false; + final String packageName = callingActivity.getPackageName(); + if (packageName == null) return false; + return packageName.equals(activity.getApplicationContext().getPackageName()); + } + @Override + public void onImportVCardConfirmed() { startImport(); } + + @Override + public void onImportVCardDenied() { + finish(); + } private void startImport() { Intent intent = getIntent(); diff --git a/src/com/android/contacts/common/vcard/ImportVCardDialogFragment.java b/src/com/android/contacts/common/vcard/ImportVCardDialogFragment.java new file mode 100644 index 00000000..ae8ebbc9 --- /dev/null +++ b/src/com/android/contacts/common/vcard/ImportVCardDialogFragment.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.contacts.common.vcard; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; + +import com.android.contacts.common.R; + +/** Asks for confirmation before importing contacts from a vcard. */ +public class ImportVCardDialogFragment extends DialogFragment { + + /** Callbacks for hosts of the {@link ImportVCardDialogFragment}. */ + public interface Listener { + + /** Invoked after the user has confirmed that contacts should be imported. */ + void onImportVCardConfirmed(); + + /** Invoked after the user has rejected importing contacts. */ + void onImportVCardDenied(); + } + + /** Displays the dialog asking for confirmation before importing contacts. */ + public static void show(Activity activity) { + if (!(activity instanceof Listener)) { + throw new IllegalArgumentException( + "Activity must implement " + Listener.class.getName()); + } + + final ImportVCardDialogFragment dialog = new ImportVCardDialogFragment(); + dialog.show(activity.getFragmentManager(), "importVCardDialogFragment"); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setMessage(R.string.import_from_vcf_file_confirmation_message) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + final Listener listener = (Listener) getActivity(); + if (listener != null) { + listener.onImportVCardConfirmed(); + } + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + final Listener listener = (Listener) getActivity(); + if (listener != null) { + listener.onImportVCardDenied(); + } + } + }) + .create(); + } +} |