1 /* 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 17 package com.android.contacts.interactions; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.app.DialogFragment; 23 import android.app.FragmentManager; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.res.Resources; 28 import android.os.Bundle; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.ArrayAdapter; 35 import android.widget.TextView; 36 37 import com.android.contacts.R; 38 import com.android.contacts.activities.SimImportActivity; 39 import com.android.contacts.compat.CompatUtils; 40 import com.android.contacts.compat.PhoneNumberUtilsCompat; 41 import com.android.contacts.database.SimContactDao; 42 import com.android.contacts.editor.SelectAccountDialogFragment; 43 import com.android.contacts.model.AccountTypeManager; 44 import com.android.contacts.model.SimCard; 45 import com.android.contacts.model.SimContact; 46 import com.android.contacts.model.account.AccountInfo; 47 import com.android.contacts.model.account.AccountWithDataSet; 48 import com.android.contacts.util.AccountSelectionUtil; 49 import com.google.common.util.concurrent.Futures; 50 51 import java.util.List; 52 import java.util.concurrent.Future; 53 54 /** 55 * An dialog invoked to import/export contacts. 56 */ 57 public class ImportDialogFragment extends DialogFragment { 58 public static final String TAG = "ImportDialogFragment"; 59 60 public static final String KEY_RES_ID = "resourceId"; 61 public static final String KEY_SUBSCRIPTION_ID = "subscriptionId"; 62 63 public static final String EXTRA_SIM_ONLY = "extraSimOnly"; 64 65 public static final String EXTRA_SIM_CONTACT_COUNT_PREFIX = "simContactCount_"; 66 67 private boolean mSimOnly = false; 68 private SimContactDao mSimDao; 69 70 private Future<List<AccountInfo>> mAccountsFuture; 71 72 /** Preferred way to show this dialog */ 73 public static void show(FragmentManager fragmentManager) { 74 final ImportDialogFragment fragment = new ImportDialogFragment(); 75 fragment.show(fragmentManager, TAG); 76 } 77 78 public static void show(FragmentManager fragmentManager, List<SimCard> sims, 79 boolean includeVcf) { 80 final ImportDialogFragment fragment = new ImportDialogFragment(); 81 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 91 fragment.setArguments(args); 92 fragment.show(fragmentManager, TAG); 93 } 94 95 @Override 96 public void onCreate(Bundle savedInstanceState) { 97 super.onCreate(savedInstanceState); 98 99 setStyle(STYLE_NORMAL, R.style.ContactsAlertDialogTheme); 100 101 final Bundle args = getArguments(); 102 mSimOnly = args != null && args.getBoolean(EXTRA_SIM_ONLY, false); 103 mSimDao = SimContactDao.create(getContext()); 104 } 105 106 @Override 107 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 116 public Context getContext() { 117 return getActivity(); 118 } 119 120 @Override 121 public void onAttach(Activity activity) { 122 super.onAttach(activity); 123 } 124 125 @Override 126 public Dialog onCreateDialog(Bundle savedInstanceState) { 127 final LayoutInflater dialogInflater = (LayoutInflater) 128 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 129 130 // Adapter that shows a list of string resources 131 final ArrayAdapter<AdapterEntry> adapter = new ArrayAdapter<AdapterEntry>(getActivity(), 132 R.layout.select_dialog_item) { 133 134 @Override 135 public View getView(int position, View convertView, ViewGroup parent) { 136 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); 152 return result; 153 } 154 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 } 183 }; 184 185 addItems(adapter); 186 187 final DialogInterface.OnClickListener clickListener = 188 new DialogInterface.OnClickListener() { 189 @Override 190 public void onClick(DialogInterface dialog, int which) { 191 final int resId = adapter.getItem(which).mChoiceResourceId; 192 if (resId == R.string.import_from_sim) { 193 handleSimImportRequest(adapter.getItem(which).mSim); 194 } else if (resId == R.string.import_from_vcf_file) { 195 handleImportRequest(resId, SimCard.NO_SUBSCRIPTION_ID); 196 } else { 197 Log.e(TAG, "Unexpected resource: " 198 + getActivity().getResources().getResourceEntryName(resId)); 199 } 200 dialog.dismiss(); 201 } 202 }; 203 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); 226 } 227 228 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), 238 R.string.import_from_sim, sims.get(0))); 239 return; 240 } 241 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)); 244 } 245 } 246 247 private void handleSimImportRequest(SimCard sim) { 248 startActivity(new Intent(getActivity(), SimImportActivity.class) 249 .putExtra(SimImportActivity.EXTRA_SUBSCRIPTION_ID, sim.getSubscriptionId())); 250 } 251 252 /** 253 * Handle "import from SD". 254 */ 255 private void handleImportRequest(int resId, int subscriptionId) { 256 // 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 262 // 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 266 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); 271 args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId); 272 SelectAccountDialogFragment.show( 273 getFragmentManager(), R.string.dialog_new_contact_account, 274 AccountTypeManager.AccountFilter.CONTACTS_WRITABLE, args); 275 } else { 276 AccountSelectionUtil.doImport(getActivity(), resId, 277 (size == 1 ? accountList.get(0) : null), 278 (CompatUtils.isMSIMCompatible() ? subscriptionId : -1)); 279 } 280 } 281 282 private CharSequence getSimDescription(SimCard sim, int index) { 283 final CharSequence name = sim.getDisplayName(); 284 if (name != null) { 285 return getString(R.string.import_from_sim_summary_fmt, name); 286 } else { 287 return getString(R.string.import_from_sim_summary_fmt, String.valueOf(index)); 288 } 289 } 290 291 private static class AdapterEntry { 292 public final CharSequence mLabel; 293 public final int mChoiceResourceId; 294 public final SimCard mSim; 295 296 public AdapterEntry(CharSequence label, int resId, SimCard sim) { 297 mLabel = label; 298 mChoiceResourceId = resId; 299 mSim = sim; 300 } 301 302 public AdapterEntry(String label, int resId) { 303 // Store a nonsense value for mSubscriptionId. If this constructor is used, 304 // the mSubscriptionId value should not be read later. 305 this(label, resId, /* sim= */ null); 306 } 307 } 308 } 309