blob: 367ae2f8d17b18c86e1d94d9a66cd54b91b7c79a [file] [log] [blame]
Chiao Cheng6fe32c02012-12-04 15:44:17 -08001/*
2 * Copyright (C) 2010 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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.interactions;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080018
Sai Cheemalapati8ee2e272014-08-06 10:30:21 -070019import android.app.Activity;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080020import android.app.AlertDialog;
21import android.app.Dialog;
Brian Attwellc2e912c2014-10-27 12:29:44 -070022import android.app.DialogFragment;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080023import android.app.FragmentManager;
24import android.content.Context;
25import android.content.DialogInterface;
Marcus Hagerotta8b448a2016-11-18 12:51:39 -080026import android.content.Intent;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080027import android.content.res.Resources;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080028import android.os.Bundle;
Brian Attwellc07c8bb2014-10-06 14:55:49 -070029import android.text.TextUtils;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080030import android.util.Log;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.ArrayAdapter;
35import android.widget.TextView;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080036
Arthur Wang3f6a2442016-12-05 14:51:59 -080037import com.android.contacts.R;
Gary Mai0a49afa2016-12-05 15:53:58 -080038import com.android.contacts.activities.SimImportActivity;
Gary Mai69c182a2016-12-05 13:07:03 -080039import com.android.contacts.compat.CompatUtils;
40import com.android.contacts.compat.PhoneNumberUtilsCompat;
41import com.android.contacts.database.SimContactDao;
Gary Mai0a49afa2016-12-05 15:53:58 -080042import com.android.contacts.editor.SelectAccountDialogFragment;
Gary Mai69c182a2016-12-05 13:07:03 -080043import com.android.contacts.model.AccountTypeManager;
44import com.android.contacts.model.SimCard;
45import com.android.contacts.model.SimContact;
Marcus Hagerotta181ca62016-12-21 14:42:13 -080046import com.android.contacts.model.account.AccountInfo;
Gary Mai69c182a2016-12-05 13:07:03 -080047import com.android.contacts.model.account.AccountWithDataSet;
48import com.android.contacts.util.AccountSelectionUtil;
Marcus Hagerotta181ca62016-12-21 14:42:13 -080049import com.google.common.util.concurrent.Futures;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080050
51import java.util.List;
Marcus Hagerotta181ca62016-12-21 14:42:13 -080052import java.util.concurrent.Future;
Chiao Cheng6fe32c02012-12-04 15:44:17 -080053
54/**
55 * An dialog invoked to import/export contacts.
56 */
Wenyi Wangc3322282016-11-11 14:53:29 -080057public class ImportDialogFragment extends DialogFragment {
James Laskey1e2102f2016-09-19 10:09:08 -070058 public static final String TAG = "ImportDialogFragment";
Wenyi Wangc33f4922015-09-24 15:28:59 -070059
Wenyi Wangc3322282016-11-11 14:53:29 -080060 public static final String KEY_RES_ID = "resourceId";
61 public static final String KEY_SUBSCRIPTION_ID = "subscriptionId";
Chiao Cheng6fe32c02012-12-04 15:44:17 -080062
Marcus Hagerott02f5dba2016-10-18 12:34:57 -070063 public static final String EXTRA_SIM_ONLY = "extraSimOnly";
64
Marcus Hagerott8636d1e2016-10-27 19:14:12 -070065 public static final String EXTRA_SIM_CONTACT_COUNT_PREFIX = "simContactCount_";
66
Marcus Hagerott02f5dba2016-10-18 12:34:57 -070067 private boolean mSimOnly = false;
Marcus Hagerott216e2972016-10-26 15:53:42 -070068 private SimContactDao mSimDao;
Wenyi Wangc33f4922015-09-24 15:28:59 -070069
Marcus Hagerotta181ca62016-12-21 14:42:13 -080070 private Future<List<AccountInfo>> mAccountsFuture;
71
Chiao Cheng6fe32c02012-12-04 15:44:17 -080072 /** Preferred way to show this dialog */
Marcus Hagerott02f5dba2016-10-18 12:34:57 -070073 public static void show(FragmentManager fragmentManager) {
74 final ImportDialogFragment fragment = new ImportDialogFragment();
75 fragment.show(fragmentManager, TAG);
76 }
77
Marcus Hagerott8636d1e2016-10-27 19:14:12 -070078 public static void show(FragmentManager fragmentManager, List<SimCard> sims,
79 boolean includeVcf) {
James Laskey1e2102f2016-09-19 10:09:08 -070080 final ImportDialogFragment fragment = new ImportDialogFragment();
Marcus Hagerott8636d1e2016-10-27 19:14:12 -070081 final Bundle args = new Bundle();
82 args.putBoolean(EXTRA_SIM_ONLY, !includeVcf);
83 for (SimCard sim : sims) {
84 final List<SimContact> contacts = sim.getContacts();
85 if (contacts == null) {
86 continue;
87 }
88 args.putInt(EXTRA_SIM_CONTACT_COUNT_PREFIX + sim.getSimId(), contacts.size());
89 }
90
Chiao Cheng6fe32c02012-12-04 15:44:17 -080091 fragment.setArguments(args);
James Laskey1e2102f2016-09-19 10:09:08 -070092 fragment.show(fragmentManager, TAG);
Chiao Cheng6fe32c02012-12-04 15:44:17 -080093 }
94
95 @Override
Marcus Hagerott02f5dba2016-10-18 12:34:57 -070096 public void onCreate(Bundle savedInstanceState) {
97 super.onCreate(savedInstanceState);
98
Sean Midford9da13ab2016-11-07 14:15:54 -080099 setStyle(STYLE_NORMAL, R.style.ContactsAlertDialogTheme);
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700100
Marcus Hagerott02f5dba2016-10-18 12:34:57 -0700101 final Bundle args = getArguments();
102 mSimOnly = args != null && args.getBoolean(EXTRA_SIM_ONLY, false);
Marcus Hagerott216e2972016-10-26 15:53:42 -0700103 mSimDao = SimContactDao.create(getContext());
Marcus Hagerott02f5dba2016-10-18 12:34:57 -0700104 }
105
106 @Override
Marcus Hagerotta181ca62016-12-21 14:42:13 -0800107 public void onResume() {
108 super.onResume();
109
110 // Start loading the accounts. This is done in onResume in case they were refreshed.
111 mAccountsFuture = AccountTypeManager.getInstance(getActivity()).filterAccountsAsync(
112 AccountTypeManager.writableFilter());
113 }
114
115 @Override
Wenyi Wang9342fbb2015-11-17 19:36:35 -0800116 public Context getContext() {
117 return getActivity();
118 }
119
120 @Override
Sai Cheemalapati8ee2e272014-08-06 10:30:21 -0700121 public void onAttach(Activity activity) {
122 super.onAttach(activity);
Sai Cheemalapati8ee2e272014-08-06 10:30:21 -0700123 }
124
125 @Override
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800126 public Dialog onCreateDialog(Bundle savedInstanceState) {
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700127 final LayoutInflater dialogInflater = (LayoutInflater)
128 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800129
130 // Adapter that shows a list of string resources
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700131 final ArrayAdapter<AdapterEntry> adapter = new ArrayAdapter<AdapterEntry>(getActivity(),
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800132 R.layout.select_dialog_item) {
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700133
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800134 @Override
135 public View getView(int position, View convertView, ViewGroup parent) {
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700136 final View result = convertView != null ? convertView :
137 dialogInflater.inflate(R.layout.select_dialog_item, parent, false);
138 final TextView primaryText = (TextView) result.findViewById(R.id.primary_text);
139 final TextView secondaryText = (TextView) result.findViewById(R.id.secondary_text);
140 final AdapterEntry entry = getItem(position);
141 secondaryText.setVisibility(View.GONE);
142 if (entry.mChoiceResourceId == R.string.import_from_sim) {
143 final CharSequence secondary = getSimSecondaryText(entry.mSim);
144 if (TextUtils.isEmpty(secondary)) {
145 secondaryText.setVisibility(View.GONE);
146 } else {
147 secondaryText.setText(secondary);
148 secondaryText.setVisibility(View.VISIBLE);
149 }
150 }
151 primaryText.setText(entry.mLabel);
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800152 return result;
153 }
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700154
155 CharSequence getSimSecondaryText(SimCard sim) {
156 int count = getSimContactCount(sim);
157
158 CharSequence phone = sim.getFormattedPhone();
159 if (phone == null) {
160 phone = sim.getPhone();
161 }
162 if (phone != null) {
163 phone = PhoneNumberUtilsCompat.createTtsSpannable(phone);
164 }
165
166 if (count != -1 && phone != null) {
167 // We use a template instead of format string so that the TTS span is preserved
168 final CharSequence template = getResources()
169 .getQuantityString(R.plurals.import_from_sim_secondary_template, count);
170 return TextUtils.expandTemplate(template, String.valueOf(count), phone);
171 } else if (phone != null) {
172 return phone;
173 } else if (count != -1) {
174 // count != -1
175 return getResources()
176 .getQuantityString(
177 R.plurals.import_from_sim_secondary_contact_count_fmt, count,
178 count);
179 } else {
180 return null;
181 }
182 }
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800183 };
184
Marcus Hagerott216e2972016-10-26 15:53:42 -0700185 addItems(adapter);
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800186
187 final DialogInterface.OnClickListener clickListener =
188 new DialogInterface.OnClickListener() {
189 @Override
190 public void onClick(DialogInterface dialog, int which) {
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700191 final int resId = adapter.getItem(which).mChoiceResourceId;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700192 if (resId == R.string.import_from_sim) {
Wenyi Wangc3322282016-11-11 14:53:29 -0800193 handleSimImportRequest(adapter.getItem(which).mSim);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700194 } else if (resId == R.string.import_from_vcf_file) {
Wenyi Wangc3322282016-11-11 14:53:29 -0800195 handleImportRequest(resId, SimCard.NO_SUBSCRIPTION_ID);
Sailesh Nepalb4a522e2016-02-20 21:17:32 -0800196 } else {
Sailesh Nepalb4a522e2016-02-20 21:17:32 -0800197 Log.e(TAG, "Unexpected resource: "
198 + getActivity().getResources().getResourceEntryName(resId));
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800199 }
Wenyi Wangc3322282016-11-11 14:53:29 -0800200 dialog.dismiss();
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800201 }
202 };
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700203
204 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), getTheme())
205 .setTitle(R.string.dialog_import)
206 .setNegativeButton(android.R.string.cancel, null);
207 if (adapter.isEmpty()) {
208 // Handle edge case; e.g. SIM card was removed.
209 builder.setMessage(R.string.nothing_to_import_message);
210 } else {
211 builder.setSingleChoiceItems(adapter, -1, clickListener);
212 }
213
214 return builder.create();
215 }
216
217 private int getSimContactCount(SimCard sim) {
218 if (sim.getContacts() != null) {
219 return sim.getContacts().size();
220 }
221 final Bundle args = getArguments();
222 if (args == null) {
223 return -1;
224 }
225 return args.getInt(EXTRA_SIM_CONTACT_COUNT_PREFIX + sim.getSimId(), -1);
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800226 }
227
Marcus Hagerott216e2972016-10-26 15:53:42 -0700228 private void addItems(ArrayAdapter<AdapterEntry> adapter) {
229 final Resources res = getActivity().getResources();
230 if (res.getBoolean(R.bool.config_allow_import_from_vcf_file) && !mSimOnly) {
231 adapter.add(new AdapterEntry(getString(R.string.import_from_vcf_file),
232 R.string.import_from_vcf_file));
233 }
234 final List<SimCard> sims = mSimDao.getSimCards();
235
236 if (sims.size() == 1) {
237 adapter.add(new AdapterEntry(getString(R.string.import_from_sim),
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700238 R.string.import_from_sim, sims.get(0)));
Marcus Hagerott216e2972016-10-26 15:53:42 -0700239 return;
240 }
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700241 for (int i = 0; i < sims.size(); i++) {
242 final SimCard sim = sims.get(i);
243 adapter.add(new AdapterEntry(getSimDescription(sim, i), R.string.import_from_sim, sim));
Marcus Hagerott216e2972016-10-26 15:53:42 -0700244 }
245 }
246
Wenyi Wangc3322282016-11-11 14:53:29 -0800247 private void handleSimImportRequest(SimCard sim) {
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800248 startActivity(new Intent(getActivity(), SimImportActivity.class)
249 .putExtra(SimImportActivity.EXTRA_SUBSCRIPTION_ID, sim.getSubscriptionId()));
Marcus Hagerott819214d2016-09-29 14:58:27 -0700250 }
251
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800252 /**
Marcus Hagerott819214d2016-09-29 14:58:27 -0700253 * Handle "import from SD".
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800254 */
Wenyi Wangc3322282016-11-11 14:53:29 -0800255 private void handleImportRequest(int resId, int subscriptionId) {
Marcus Hagerotta181ca62016-12-21 14:42:13 -0800256 // Get the accounts. Because this only happens after a user action this should pretty
257 // much never block since it will usually be at least several seconds before the user
258 // interacts with the view
259 final List<AccountWithDataSet> accountList = AccountInfo.extractAccounts(
260 Futures.getUnchecked(mAccountsFuture));
261
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800262 // There are three possibilities:
263 // - more than one accounts -> ask the user
264 // - just one account -> use the account without asking the user
265 // - no account -> use phone-local storage without asking the user
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800266 final int size = accountList.size();
267 if (size > 1) {
268 // Send over to the account selector
269 final Bundle args = new Bundle();
270 args.putInt(KEY_RES_ID, resId);
Jay Shraunercf082222014-11-25 13:52:50 -0800271 args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800272 SelectAccountDialogFragment.show(
Wenyi Wangc3322282016-11-11 14:53:29 -0800273 getFragmentManager(), R.string.dialog_new_contact_account,
Marcus Hagerottee3d3a52016-12-22 10:35:30 -0800274 AccountTypeManager.AccountFilter.CONTACTS_WRITABLE, args);
Wenyi Wangc3322282016-11-11 14:53:29 -0800275 } else {
276 AccountSelectionUtil.doImport(getActivity(), resId,
277 (size == 1 ? accountList.get(0) : null),
278 (CompatUtils.isMSIMCompatible() ? subscriptionId : -1));
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800279 }
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800280 }
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700281
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700282 private CharSequence getSimDescription(SimCard sim, int index) {
Marcus Hagerott216e2972016-10-26 15:53:42 -0700283 final CharSequence name = sim.getDisplayName();
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700284 if (name != null) {
Marcus Hagerott216e2972016-10-26 15:53:42 -0700285 return getString(R.string.import_from_sim_summary_fmt, name);
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700286 } else {
287 return getString(R.string.import_from_sim_summary_fmt, String.valueOf(index));
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700288 }
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700289 }
290
291 private static class AdapterEntry {
Ihab Awad37321802014-12-05 16:23:11 -0800292 public final CharSequence mLabel;
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700293 public final int mChoiceResourceId;
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700294 public final SimCard mSim;
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700295
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700296 public AdapterEntry(CharSequence label, int resId, SimCard sim) {
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700297 mLabel = label;
298 mChoiceResourceId = resId;
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700299 mSim = sim;
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700300 }
301
302 public AdapterEntry(String label, int resId) {
Brian Attwell87ffb822015-02-03 10:28:11 -0800303 // Store a nonsense value for mSubscriptionId. If this constructor is used,
304 // the mSubscriptionId value should not be read later.
Marcus Hagerott8636d1e2016-10-27 19:14:12 -0700305 this(label, resId, /* sim= */ null);
Brian Attwellc07c8bb2014-10-06 14:55:49 -0700306 }
307 }
Chiao Cheng6fe32c02012-12-04 15:44:17 -0800308}