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