SimSettings.java revision b153181c9c597085769d69796ecf07ec6d4433d2
1/* 2 * Copyright (C) 2014 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 17package com.android.settings.sim; 18 19import android.app.AlertDialog; 20import android.content.Context; 21import android.content.ContentUris; 22import android.content.DialogInterface; 23import android.content.res.Resources; 24import android.database.Cursor; 25import android.net.Uri; 26import android.os.Bundle; 27import android.preference.Preference; 28import android.preference.PreferenceCategory; 29import android.preference.PreferenceScreen; 30import android.provider.SearchIndexableResource; 31import android.provider.Telephony; 32import android.telephony.SubInfoRecord; 33import android.telephony.SubscriptionManager; 34import android.telephony.TelephonyManager; 35import android.telephony.PhoneNumberUtils; 36import android.telecom.PhoneAccount; 37import android.telecom.PhoneAccountHandle; 38import android.telecom.TelecomManager; 39import android.text.TextUtils; 40import android.util.Log; 41import android.view.LayoutInflater; 42import android.view.View; 43import android.widget.ArrayAdapter; 44import android.widget.EditText; 45import android.widget.ImageView; 46import android.widget.Spinner; 47import android.widget.TextView; 48import android.app.Dialog; 49import android.view.ViewGroup; 50import android.widget.AdapterView; 51import android.widget.ListAdapter; 52 53import com.android.internal.telephony.PhoneFactory; 54import com.android.settings.RestrictedSettingsFragment; 55import com.android.settings.Utils; 56import com.android.settings.search.BaseSearchIndexProvider; 57import com.android.settings.search.Indexable; 58import com.android.settings.R; 59 60import java.util.ArrayList; 61import java.util.Collections; 62import java.util.Comparator; 63import java.util.Iterator; 64import java.util.List; 65 66public class SimSettings extends RestrictedSettingsFragment implements Indexable { 67 private static final String TAG = "SimSettings"; 68 69 private static final String DISALLOW_CONFIG_SIM = "no_config_sim"; 70 private static final String SIM_CARD_CATEGORY = "sim_cards"; 71 private static final String KEY_CELLULAR_DATA = "sim_cellular_data"; 72 private static final String KEY_CALLS = "sim_calls"; 73 private static final String KEY_SMS = "sim_sms"; 74 private static final String KEY_ACTIVITIES = "activities"; 75 private static final int ID_INDEX = 0; 76 private static final int NAME_INDEX = 1; 77 private static final int APN_INDEX = 2; 78 private static final int PROXY_INDEX = 3; 79 private static final int PORT_INDEX = 4; 80 private static final int USER_INDEX = 5; 81 private static final int SERVER_INDEX = 6; 82 private static final int PASSWORD_INDEX = 7; 83 private static final int MMSC_INDEX = 8; 84 private static final int MCC_INDEX = 9; 85 private static final int MNC_INDEX = 10; 86 private static final int NUMERIC_INDEX = 11; 87 private static final int MMSPROXY_INDEX = 12; 88 private static final int MMSPORT_INDEX = 13; 89 private static final int AUTH_TYPE_INDEX = 14; 90 private static final int TYPE_INDEX = 15; 91 private static final int PROTOCOL_INDEX = 16; 92 private static final int CARRIER_ENABLED_INDEX = 17; 93 private static final int BEARER_INDEX = 18; 94 private static final int ROAMING_PROTOCOL_INDEX = 19; 95 private static final int MVNO_TYPE_INDEX = 20; 96 private static final int MVNO_MATCH_DATA_INDEX = 21; 97 private static final int DATA_PICK = 0; 98 private static final int CALLS_PICK = 1; 99 private static final int SMS_PICK = 2; 100 101 /** 102 * By UX design we use only one Subscription Information(SubInfo) record per SIM slot. 103 * mAvalableSubInfos is the list of SubInfos we present to the user. 104 * mSubInfoList is the list of all SubInfos. 105 * mSelectableSubInfos is the list of SubInfos that a user can select for data, calls, and SMS. 106 */ 107 private List<SubInfoRecord> mAvailableSubInfos = null; 108 private List<SubInfoRecord> mSubInfoList = null; 109 private List<SubInfoRecord> mSelectableSubInfos = null; 110 111 private SubInfoRecord mCellularData = null; 112 private SubInfoRecord mCalls = null; 113 private SubInfoRecord mSMS = null; 114 115 private PreferenceCategory mSimCards = null; 116 117 private int mNumSims; 118 /** 119 * Standard projection for the interesting columns of a normal note. 120 */ 121 private static final String[] sProjection = new String[] { 122 Telephony.Carriers._ID, // 0 123 Telephony.Carriers.NAME, // 1 124 Telephony.Carriers.APN, // 2 125 Telephony.Carriers.PROXY, // 3 126 Telephony.Carriers.PORT, // 4 127 Telephony.Carriers.USER, // 5 128 Telephony.Carriers.SERVER, // 6 129 Telephony.Carriers.PASSWORD, // 7 130 Telephony.Carriers.MMSC, // 8 131 Telephony.Carriers.MCC, // 9 132 Telephony.Carriers.MNC, // 10 133 Telephony.Carriers.NUMERIC, // 11 134 Telephony.Carriers.MMSPROXY,// 12 135 Telephony.Carriers.MMSPORT, // 13 136 Telephony.Carriers.AUTH_TYPE, // 14 137 Telephony.Carriers.TYPE, // 15 138 Telephony.Carriers.PROTOCOL, // 16 139 Telephony.Carriers.CARRIER_ENABLED, // 17 140 Telephony.Carriers.BEARER, // 18 141 Telephony.Carriers.ROAMING_PROTOCOL, // 19 142 Telephony.Carriers.MVNO_TYPE, // 20 143 Telephony.Carriers.MVNO_MATCH_DATA // 21 144 }; 145 146 public SimSettings() { 147 super(DISALLOW_CONFIG_SIM); 148 } 149 150 @Override 151 public void onCreate(final Bundle bundle) { 152 super.onCreate(bundle); 153 154 if (mSubInfoList == null) { 155 mSubInfoList = SubscriptionManager.getActiveSubInfoList(); 156 } 157 158 createPreferences(); 159 updateAllOptions(); 160 } 161 162 private void createPreferences() { 163 final TelephonyManager tm = 164 (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 165 166 addPreferencesFromResource(R.xml.sim_settings); 167 168 mSimCards = (PreferenceCategory)findPreference(SIM_CARD_CATEGORY); 169 170 final int numSlots = tm.getSimCount(); 171 mAvailableSubInfos = new ArrayList<SubInfoRecord>(numSlots); 172 mSelectableSubInfos = new ArrayList<SubInfoRecord>(); 173 mNumSims = 0; 174 for (int i = 0; i < numSlots; ++i) { 175 final SubInfoRecord sir = findRecordBySlotId(i); 176 mSimCards.addPreference(new SimPreference(getActivity(), sir, i)); 177 mAvailableSubInfos.add(sir); 178 if (sir != null) { 179 mNumSims++; 180 mSelectableSubInfos.add(sir); 181 } 182 } 183 184 updateActivitesCategory(); 185 } 186 187 private void updateAvailableSubInfos(){ 188 final TelephonyManager tm = 189 (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); 190 final int numSlots = tm.getSimCount(); 191 192 mNumSims = 0; 193 mAvailableSubInfos = new ArrayList<SubInfoRecord>(numSlots); 194 for (int i = 0; i < numSlots; ++i) { 195 final SubInfoRecord sir = findRecordBySlotId(i); 196 mAvailableSubInfos.add(sir); 197 if (sir != null) { 198 mNumSims++; 199 } 200 } 201 } 202 203 private void updateAllOptions() { 204 updateSimSlotValues(); 205 updateActivitesCategory(); 206 } 207 208 private void updateSimSlotValues() { 209 SubscriptionManager.getAllSubInfoList(); 210 211 final int prefSize = mSimCards.getPreferenceCount(); 212 for (int i = 0; i < prefSize; ++i) { 213 Preference pref = mSimCards.getPreference(i); 214 if (pref instanceof SimPreference) { 215 ((SimPreference)pref).update(); 216 } 217 } 218 } 219 220 private void updateActivitesCategory() { 221 updateCellularDataValues(); 222 updateCallValues(); 223 updateSmsValues(); 224 } 225 226 /** 227 * finds a record with subId. 228 * Since the number of SIMs are few, an array is fine. 229 */ 230 private SubInfoRecord findRecordBySubId(final int subId) { 231 final int availableSubInfoLength = mAvailableSubInfos.size(); 232 233 for (int i = 0; i < availableSubInfoLength; ++i) { 234 final SubInfoRecord sir = mAvailableSubInfos.get(i); 235 if (sir != null && sir.getSubscriptionId() == subId) { 236 return sir; 237 } 238 } 239 240 return null; 241 } 242 243 /** 244 * finds a record with slotId. 245 * Since the number of SIMs are few, an array is fine. 246 */ 247 private SubInfoRecord findRecordBySlotId(final int slotId) { 248 if (mSubInfoList != null){ 249 final int availableSubInfoLength = mSubInfoList.size(); 250 251 for (int i = 0; i < availableSubInfoLength; ++i) { 252 final SubInfoRecord sir = mSubInfoList.get(i); 253 if (sir.getSimSlotIndex() == slotId) { 254 //Right now we take the first subscription on a SIM. 255 return sir; 256 } 257 } 258 } 259 260 return null; 261 } 262 263 private void updateSmsValues() { 264 final Preference simPref = (Preference) findPreference(KEY_SMS); 265 final SubInfoRecord sir = findRecordBySubId(SubscriptionManager.getDefaultSmsSubId()); 266 simPref.setTitle(R.string.sms_messages_title); 267 if (mSubInfoList.size() == 1) { 268 simPref.setSummary(mSubInfoList.get(0).getDisplayName()); 269 } else if (sir != null) { 270 simPref.setSummary(sir.getDisplayName()); 271 } else if (sir == null) { 272 simPref.setSummary(R.string.sim_selection_required_pref); 273 } 274 simPref.setEnabled(mNumSims >= 1); 275 } 276 277 private void updateCellularDataValues() { 278 final Preference simPref = findPreference(KEY_CELLULAR_DATA); 279 final SubInfoRecord sir = findRecordBySubId(SubscriptionManager.getDefaultDataSubId()); 280 simPref.setTitle(R.string.cellular_data_title); 281 if (mSubInfoList.size() == 1) { 282 simPref.setSummary(mSubInfoList.get(0).getDisplayName()); 283 } else if (sir != null) { 284 simPref.setSummary(sir.getDisplayName()); 285 } else if (sir == null) { 286 simPref.setSummary(R.string.sim_selection_required_pref); 287 } 288 simPref.setEnabled(mNumSims >= 1); 289 } 290 291 private void updateCallValues() { 292 final Preference simPref = findPreference(KEY_CALLS); 293 final TelecomManager telecomManager = TelecomManager.from(getActivity()); 294 final PhoneAccountHandle phoneAccount = 295 telecomManager.getUserSelectedOutgoingPhoneAccount(); 296 297 simPref.setTitle(R.string.calls_title); 298 simPref.setSummary(phoneAccount == null 299 ? getResources().getString(R.string.sim_selection_required_pref) 300 : (String)telecomManager.getPhoneAccount(phoneAccount).getLabel()); 301 } 302 303 @Override 304 public void onResume() { 305 super.onResume(); 306 307 mSubInfoList = SubscriptionManager.getActiveSubInfoList(); 308 updateAvailableSubInfos(); 309 updateAllOptions(); 310 } 311 312 @Override 313 public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen, 314 final Preference preference) { 315 if (preference instanceof SimPreference) { 316 ((SimPreference)preference).createEditDialog((SimPreference)preference); 317 } else if ((Preference) findPreference(KEY_CELLULAR_DATA) == preference) { 318 showDialog(DATA_PICK); 319 } else if ((Preference) findPreference(KEY_CALLS) == preference) { 320 showDialog(CALLS_PICK); 321 } else if ((Preference) findPreference(KEY_SMS) == preference) { 322 showDialog(SMS_PICK); 323 } 324 325 return true; 326 } 327 328 @Override 329 public Dialog onCreateDialog(final int id) { 330 final ArrayList<String> list = new ArrayList<String>(); 331 final int selectableSubInfoLength = mSelectableSubInfos.size(); 332 333 final DialogInterface.OnClickListener selectionListener = 334 new DialogInterface.OnClickListener() { 335 @Override 336 public void onClick(DialogInterface dialog, int value) { 337 338 final SubInfoRecord sir; 339 340 if (id == DATA_PICK) { 341 sir = mSelectableSubInfos.get(value); 342 SubscriptionManager.setDefaultDataSubId(sir.getSubscriptionId()); 343 } else if (id == CALLS_PICK) { 344 final TelecomManager telecomManager = 345 TelecomManager.from(getActivity()); 346 final List<PhoneAccountHandle> phoneAccountsList = 347 telecomManager.getCallCapablePhoneAccounts(); 348 telecomManager.setUserSelectedOutgoingPhoneAccount( 349 value < 1 ? null : phoneAccountsList.get(value - 1)); 350 } else if (id == SMS_PICK) { 351 sir = mSelectableSubInfos.get(value); 352 SubscriptionManager.setDefaultSmsSubId(sir.getSubscriptionId()); 353 } 354 355 updateActivitesCategory(); 356 } 357 }; 358 359 if (id == CALLS_PICK) { 360 final TelecomManager telecomManager = TelecomManager.from(getActivity()); 361 final Iterator<PhoneAccountHandle> phoneAccounts = 362 telecomManager.getCallCapablePhoneAccounts().listIterator(); 363 364 list.add(getResources().getString(R.string.sim_calls_ask_first_prefs_title)); 365 while (phoneAccounts.hasNext()) { 366 final PhoneAccount phoneAccount = 367 telecomManager.getPhoneAccount(phoneAccounts.next()); 368 list.add((String)phoneAccount.getLabel()); 369 } 370 } else { 371 for (int i = 0; i < selectableSubInfoLength; ++i) { 372 final SubInfoRecord sir = mSelectableSubInfos.get(i); 373 CharSequence displayName = sir.getDisplayName(); 374 if (displayName == null) { 375 displayName = ""; 376 } 377 list.add(displayName.toString()); 378 } 379 } 380 381 String[] arr = new String[selectableSubInfoLength]; 382 arr = list.toArray(arr); 383 384 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 385 386 ListAdapter adapter = new SelectAccountListAdapter( 387 builder.getContext(), 388 R.layout.select_account_list_item, 389 arr); 390 391 if (id == DATA_PICK) { 392 builder.setTitle(R.string.select_sim_for_data); 393 } else if (id == CALLS_PICK) { 394 builder.setTitle(R.string.select_sim_for_calls); 395 } else if (id == SMS_PICK) { 396 builder.setTitle(R.string.sim_card_select_title); 397 } 398 399 return builder.setAdapter(adapter, selectionListener) 400 .create(); 401 } 402 403 private class SelectAccountListAdapter extends ArrayAdapter<String> { 404 private Context mContext; 405 private int mResId; 406 407 public SelectAccountListAdapter( 408 Context context, int resource, String[] arr) { 409 super(context, resource, arr); 410 mContext = context; 411 mResId = resource; 412 } 413 414 @Override 415 public View getView(int position, View convertView, ViewGroup parent) { 416 LayoutInflater inflater = (LayoutInflater) 417 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 418 419 View rowView; 420 final ViewHolder holder; 421 422 if (convertView == null) { 423 // Cache views for faster scrolling 424 rowView = inflater.inflate(mResId, null); 425 holder = new ViewHolder(); 426 holder.textView = (TextView) rowView.findViewById(R.id.text); 427 holder.imageView = (ImageView) rowView.findViewById(R.id.icon); 428 rowView.setTag(holder); 429 } 430 else { 431 rowView = convertView; 432 holder = (ViewHolder) rowView.getTag(); 433 } 434 435 holder.textView.setText(getItem(position)); 436 holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.ic_sim_sd)); 437 return rowView; 438 } 439 440 private class ViewHolder { 441 TextView textView; 442 ImageView imageView; 443 } 444 } 445 446 private void setActivity(Preference preference, SubInfoRecord sir) { 447 final String key = preference.getKey(); 448 449 if (key.equals(KEY_CELLULAR_DATA)) { 450 mCellularData = sir; 451 } else if (key.equals(KEY_CALLS)) { 452 mCalls = sir; 453 } else if (key.equals(KEY_SMS)) { 454 mSMS = sir; 455 } 456 457 updateActivitesCategory(); 458 } 459 460 private class SimPreference extends Preference{ 461 private SubInfoRecord mSubInfoRecord; 462 private int mSlotId; 463 private int[] colorArr; 464 465 public SimPreference(Context context, SubInfoRecord subInfoRecord, int slotId) { 466 super(context); 467 468 mSubInfoRecord = subInfoRecord; 469 mSlotId = slotId; 470 setKey("sim" + mSlotId); 471 update(); 472 colorArr = context.getResources().getIntArray(R.array.sim_colors); 473 } 474 475 public void update() { 476 final Resources res = getResources(); 477 478 if (mSubInfoRecord != null) { 479 if(TextUtils.isEmpty(mSubInfoRecord.getDisplayName())) { 480 setTitle(getCarrierName()); 481 String displayName = getCarrierName(); 482 mSubInfoRecord.setDisplayName(displayName); 483 SubscriptionManager.setDisplayName(displayName, 484 mSubInfoRecord.getSubscriptionId()); 485 } else { 486 setTitle(mSubInfoRecord.getDisplayName()); 487 } 488 setSummary(mSubInfoRecord.getNumber().toString()); 489 setEnabled(true); 490 } else { 491 setSummary(R.string.sim_slot_empty); 492 setFragment(null); 493 setEnabled(false); 494 } 495 } 496 497 public String getCarrierName() { 498 Uri mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, 499 mSubInfoRecord.getSubscriptionId()); 500 Cursor mCursor = getActivity().managedQuery(mUri, sProjection, null, null); 501 mCursor.moveToFirst(); 502 return mCursor.getString(1); 503 } 504 505 public String getFormattedPhoneNumber() { 506 try{ 507 final String rawNumber = PhoneFactory.getPhone(mSlotId).getLine1Number(); 508 String formattedNumber = null; 509 if (!TextUtils.isEmpty(rawNumber)) { 510 formattedNumber = PhoneNumberUtils.formatNumber(rawNumber); 511 } 512 513 return formattedNumber; 514 } catch (java.lang.IllegalStateException ise){ 515 return "Unknown"; 516 } 517 } 518 519 public SubInfoRecord getSubInfoRecord() { 520 return mSubInfoRecord; 521 } 522 523 public void createEditDialog(SimPreference simPref) { 524 final Resources res = getResources(); 525 526 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 527 528 final View dialogLayout = getActivity().getLayoutInflater().inflate( 529 R.layout.multi_sim_dialog, null); 530 builder.setView(dialogLayout); 531 532 EditText nameText = (EditText)dialogLayout.findViewById(R.id.sim_name); 533 nameText.setText(mSubInfoRecord.getDisplayName()); 534 535 final Spinner colorSpinner = (Spinner) dialogLayout.findViewById(R.id.spinner); 536 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getContext(), 537 R.array.color_picker, android.R.layout.simple_spinner_item); 538 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 539 colorSpinner.setAdapter(adapter); 540 541 for (int i = 0; i < colorArr.length; i++) { 542 if (colorArr[i] == mSubInfoRecord.getColor()) { 543 colorSpinner.setSelection(i); 544 break; 545 } 546 } 547 548 colorSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 549 @Override 550 public void onItemSelected(AdapterView<?> parent, View view, 551 int pos, long id){ 552 colorSpinner.setSelection(pos); 553 } 554 555 @Override 556 public void onNothingSelected(AdapterView<?> parent) { 557 } 558 }); 559 560 TextView numberView = (TextView)dialogLayout.findViewById(R.id.number); 561 numberView.setText(simPref.getFormattedPhoneNumber()); 562 563 TextView carrierView = (TextView)dialogLayout.findViewById(R.id.carrier); 564 carrierView.setText(getCarrierName()); 565 566 builder.setTitle(String.format(res.getString(R.string.sim_editor_title), 567 (mSubInfoRecord.getSimSlotIndex() + 1))); 568 569 builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { 570 @Override 571 public void onClick(DialogInterface dialog, int whichButton) { 572 final EditText nameText = (EditText)dialogLayout.findViewById(R.id.sim_name); 573 574 String displayName = nameText.getText().toString(); 575 int subId = mSubInfoRecord.getSubscriptionId(); 576 mSubInfoRecord.setDisplayName(displayName); 577 SubscriptionManager.setDisplayName(displayName, subId); 578 findRecordBySubId(subId).setDisplayName(displayName); 579 580 final int colorSelected = colorSpinner.getSelectedItemPosition(); 581 int subscriptionId = mSubInfoRecord.getSubscriptionId(); 582 int color = colorArr[colorSelected]; 583 mSubInfoRecord.setColor(color); 584 SubscriptionManager.setColor(color, subscriptionId); 585 findRecordBySubId(subscriptionId).setColor(color); 586 587 updateAllOptions(); 588 update(); 589 } 590 }); 591 592 builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 593 @Override 594 public void onClick(DialogInterface dialog, int whichButton) { 595 dialog.dismiss(); 596 } 597 }); 598 599 builder.create().show(); 600 } 601 } 602 603 /** 604 * Sort Subscription List in SIM Id, Subscription Id 605 * @param context The Context 606 * @return Sorted Subscription List or NULL if no activated Subscription 607 */ 608 public static List<SubInfoRecord> getSortedSubInfoList(Context context) { 609 List<SubInfoRecord> infoList = SubscriptionManager.getActiveSubInfoList(); 610 if (infoList != null) { 611 Collections.sort(infoList, new Comparator<SubInfoRecord>() { 612 613 @Override 614 public int compare(SubInfoRecord arg0, SubInfoRecord arg1) { 615 int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex(); 616 if (flag == 0) { 617 return (int) (arg0.getSubscriptionId() - arg1.getSubscriptionId()); 618 } 619 return flag; 620 } 621 }); 622 } 623 return infoList; 624 } 625 626 /** 627 * For search 628 */ 629 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 630 new BaseSearchIndexProvider() { 631 @Override 632 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, 633 boolean enabled) { 634 ArrayList<SearchIndexableResource> result = 635 new ArrayList<SearchIndexableResource>(); 636 637 if (Utils.showSimCardTile(context)) { 638 SearchIndexableResource sir = new SearchIndexableResource(context); 639 sir.xmlResId = R.xml.sim_settings; 640 result.add(sir); 641 } 642 643 return result; 644 } 645 }; 646 647} 648