Skip to content

Commit a4d49bc

Browse files
Walter Jangandi34
authored andcommitted
Ask for confirmation before importing from vcard
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)
1 parent b2e8cd6 commit a4d49bc

File tree

4 files changed

+135
-21
lines changed

4 files changed

+135
-21
lines changed

res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,9 @@ a ren't members of any other group. [CHAR LIMIT=25] -->
421421
<!-- Action string for selecting (USB) storage for importing contacts [CHAR LIMIT=30] -->
422422
<string name="import_from_sdcard" product="default">Import from storage</string>
423423

424+
<!-- Dialog message asking the user for confirmation before starting to import contacts from a .vcf file. [CHAR LIMIT=NONE] -->
425+
<string name="import_from_vcf_file_confirmation_message" product="default">Import contacts from vCard?</string>
426+
424427
<!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard import.
425428
The argument is file name for the vCard import the user wants to cancel.
426429
[CHAR LIMIT=128] -->

src/com/android/contacts/common/util/AccountSelectionUtil.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.android.contacts.common.util;
1818

19+
import android.app.Activity;
1920
import android.app.AlertDialog;
2021
import android.app.Dialog;
2122
import android.content.Context;
@@ -52,44 +53,44 @@ public class AccountSelectionUtil {
5253
public static class AccountSelectedListener
5354
implements DialogInterface.OnClickListener {
5455

55-
final private Context mContext;
56+
final private Activity mActivity;
5657
final private int mResId;
5758

5859
final protected List<AccountWithDataSet> mAccountList;
5960

60-
public AccountSelectedListener(Context context, List<AccountWithDataSet> accountList,
61+
public AccountSelectedListener(Activity activity, List<AccountWithDataSet> accountList,
6162
int resId) {
6263
if (accountList == null || accountList.size() == 0) {
6364
Log.e(LOG_TAG, "The size of Account list is 0.");
6465
}
65-
mContext = context;
66+
mActivity = activity;
6667
mAccountList = accountList;
6768
mResId = resId;
6869
}
6970

7071
public void onClick(DialogInterface dialog, int which) {
7172
dialog.dismiss();
72-
doImport(mContext, mResId, mAccountList.get(which));
73+
doImport(mActivity, mResId, mAccountList.get(which));
7374
}
7475
}
7576

76-
public static Dialog getSelectAccountDialog(Context context, int resId) {
77-
return getSelectAccountDialog(context, resId, null, null);
77+
public static Dialog getSelectAccountDialog(Activity activity, int resId) {
78+
return getSelectAccountDialog(activity, resId, null, null);
7879
}
7980

80-
public static Dialog getSelectAccountDialog(Context context, int resId,
81+
public static Dialog getSelectAccountDialog(Activity activity, int resId,
8182
DialogInterface.OnClickListener onClickListener) {
82-
return getSelectAccountDialog(context, resId, onClickListener, null);
83+
return getSelectAccountDialog(activity, resId, onClickListener, null);
8384
}
8485

8586
/**
8687
* When OnClickListener or OnCancelListener is null, uses a default listener.
8788
* The default OnCancelListener just closes itself with {@link Dialog#dismiss()}.
8889
*/
89-
public static Dialog getSelectAccountDialog(Context context, int resId,
90+
public static Dialog getSelectAccountDialog(Activity activity, int resId,
9091
DialogInterface.OnClickListener onClickListener,
9192
DialogInterface.OnCancelListener onCancelListener) {
92-
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
93+
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(activity);
9394
final List<AccountWithDataSet> writableAccountList = accountTypes.getAccounts(true);
9495

9596
Log.i(LOG_TAG, "The number of available accounts: " + writableAccountList.size());
@@ -98,11 +99,11 @@ public static Dialog getSelectAccountDialog(Context context, int resId,
9899

99100
// Wrap our context to inflate list items using correct theme
100101
final Context dialogContext = new ContextThemeWrapper(
101-
context, android.R.style.Theme_Light);
102+
activity, android.R.style.Theme_Light);
102103
final LayoutInflater dialogInflater = (LayoutInflater)dialogContext
103104
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
104105
final ArrayAdapter<AccountWithDataSet> accountAdapter =
105-
new ArrayAdapter<AccountWithDataSet>(context, android.R.layout.simple_list_item_2,
106+
new ArrayAdapter<AccountWithDataSet>(activity, android.R.layout.simple_list_item_2,
106107
writableAccountList) {
107108

108109
@Override
@@ -133,7 +134,7 @@ public View getView(int position, View convertView, ViewGroup parent) {
133134

134135
if (onClickListener == null) {
135136
AccountSelectedListener accountSelectedListener =
136-
new AccountSelectedListener(context, writableAccountList, resId);
137+
new AccountSelectedListener(activity, writableAccountList, resId);
137138
onClickListener = accountSelectedListener;
138139
}
139140
if (onCancelListener == null) {
@@ -143,21 +144,21 @@ public void onCancel(DialogInterface dialog) {
143144
}
144145
};
145146
}
146-
return new AlertDialog.Builder(context)
147+
return new AlertDialog.Builder(activity)
147148
.setTitle(R.string.dialog_new_contact_account)
148149
.setSingleChoiceItems(accountAdapter, 0, onClickListener)
149150
.setOnCancelListener(onCancelListener)
150151
.create();
151152
}
152153

153-
public static void doImport(Context context, int resId, AccountWithDataSet account) {
154+
public static void doImport(Activity activity, int resId, AccountWithDataSet account) {
154155
switch (resId) {
155156
case R.string.import_from_sim: {
156-
doImportFromSim(context, account);
157+
doImportFromSim(activity, account);
157158
break;
158159
}
159160
case R.string.import_from_sdcard: {
160-
doImportFromSdCard(context, account);
161+
doImportFromSdCard(activity, account);
161162
break;
162163
}
163164
}
@@ -175,8 +176,8 @@ public static void doImportFromSim(Context context, AccountWithDataSet account)
175176
context.startActivity(importIntent);
176177
}
177178

178-
public static void doImportFromSdCard(Context context, AccountWithDataSet account) {
179-
Intent importIntent = new Intent(context, ImportVCardActivity.class);
179+
public static void doImportFromSdCard(Activity activity, AccountWithDataSet account) {
180+
Intent importIntent = new Intent(activity, ImportVCardActivity.class);
180181
if (account != null) {
181182
importIntent.putExtra("account_name", account.name);
182183
importIntent.putExtra("account_type", account.type);
@@ -189,6 +190,6 @@ public static void doImportFromSdCard(Context context, AccountWithDataSet accoun
189190
}
190191
mVCardShare = false;
191192
mPath = null;
192-
context.startActivity(importIntent);
193+
activity.startActivityForResult(importIntent, 0);
193194
}
194195
}

src/com/android/contacts/common/vcard/ImportVCardActivity.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
* any Dialog in the instance. So this code is careless about the management around managed
8686
* dialogs stuffs (like how onCreateDialog() is used).
8787
*/
88-
public class ImportVCardActivity extends Activity {
88+
public class ImportVCardActivity extends Activity implements ImportVCardDialogFragment.Listener {
8989
private static final String LOG_TAG = "VCardImport";
9090

9191
private static final int SELECT_ACCOUNT = 0;
@@ -854,9 +854,43 @@ protected void onCreate(Bundle bundle) {
854854
}
855855
}
856856

857+
if (isCallerSelf(this)) {
858+
startImport();
859+
} else {
860+
ImportVCardDialogFragment.show(this);
861+
}
862+
}
863+
864+
private static boolean isCallerSelf(Activity activity) {
865+
// {@link Activity#getCallingActivity()} is a safer alternative to
866+
// {@link Activity#getCallingPackage()} that works around a
867+
// framework bug where getCallingPackage() can sometimes return null even when the
868+
// current activity *was* in fact launched via a startActivityForResult() call.
869+
//
870+
// (The bug happens if the task stack needs to be re-created by the framework after
871+
// having been killed due to memory pressure or by the "Don't keep activities"
872+
// developer option; see bug 7494866 for the full details.)
873+
//
874+
// Turns out that {@link Activity#getCallingActivity()} *does* return correct info
875+
// even in the case where getCallingPackage() is broken, so the workaround is simply
876+
// to get the package name from getCallingActivity().getPackageName() instead.
877+
final ComponentName callingActivity = activity.getCallingActivity();
878+
if (callingActivity == null) return false;
879+
final String packageName = callingActivity.getPackageName();
880+
if (packageName == null) return false;
881+
return packageName.equals(activity.getApplicationContext().getPackageName());
882+
}
883+
884+
@Override
885+
public void onImportVCardConfirmed() {
857886
startImport();
858887
}
859888

889+
@Override
890+
public void onImportVCardDenied() {
891+
finish();
892+
}
893+
860894
@Override
861895
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
862896
if (requestCode == SELECT_ACCOUNT) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (C) 2016 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.app.DialogFragment;
22+
import android.content.DialogInterface;
23+
import android.os.Bundle;
24+
25+
import com.android.contacts.common.R;
26+
27+
/** Asks for confirmation before importing contacts from a vcard. */
28+
public class ImportVCardDialogFragment extends DialogFragment {
29+
30+
/** Callbacks for hosts of the {@link ImportVCardDialogFragment}. */
31+
public interface Listener {
32+
33+
/** Invoked after the user has confirmed that contacts should be imported. */
34+
void onImportVCardConfirmed();
35+
36+
/** Invoked after the user has rejected importing contacts. */
37+
void onImportVCardDenied();
38+
}
39+
40+
/** Displays the dialog asking for confirmation before importing contacts. */
41+
public static void show(Activity activity) {
42+
if (!(activity instanceof Listener)) {
43+
throw new IllegalArgumentException(
44+
"Activity must implement " + Listener.class.getName());
45+
}
46+
47+
final ImportVCardDialogFragment dialog = new ImportVCardDialogFragment();
48+
dialog.show(activity.getFragmentManager(), "importVCardDialogFragment");
49+
}
50+
51+
@Override
52+
public Dialog onCreateDialog(Bundle savedInstanceState) {
53+
return new AlertDialog.Builder(getActivity())
54+
.setIconAttribute(android.R.attr.alertDialogIcon)
55+
.setMessage(R.string.import_from_vcf_file_confirmation_message)
56+
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
57+
@Override
58+
public void onClick(DialogInterface dialog, int whichButton) {
59+
final Listener listener = (Listener) getActivity();
60+
if (listener != null) {
61+
listener.onImportVCardConfirmed();
62+
}
63+
}
64+
})
65+
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
66+
@Override
67+
public void onClick(DialogInterface dialog, int whichButton) {
68+
final Listener listener = (Listener) getActivity();
69+
if (listener != null) {
70+
listener.onImportVCardDenied();
71+
}
72+
}
73+
})
74+
.create();
75+
}
76+
}

0 commit comments

Comments
 (0)