• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.phone;
18 
19 import android.app.ProgressDialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.os.AsyncResult;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.RemoteException;
28 import android.preference.ListPreference;
29 import android.preference.Preference;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 import android.text.BidiFormatter;
33 import android.text.TextDirectionHeuristics;
34 import android.text.TextUtils;
35 import android.util.AttributeSet;
36 import android.util.Log;
37 
38 import com.android.internal.telephony.OperatorInfo;
39 import com.android.internal.telephony.Phone;
40 import com.android.internal.telephony.PhoneFactory;
41 
42 import java.util.List;
43 
44 
45 /**
46  * "Networks" preference in "Mobile network" settings UI for the Phone app.
47  * It's used to manually search and choose mobile network. Enabled only when
48  * autoSelect preference is turned off.
49  */
50 public class NetworkSelectListPreference extends ListPreference
51         implements DialogInterface.OnCancelListener,
52         Preference.OnPreferenceChangeListener{
53 
54     private static final String LOG_TAG = "networkSelect";
55     private static final boolean DBG = true;
56 
57     private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
58     private static final int EVENT_NETWORK_SELECTION_DONE = 200;
59 
60     //dialog ids
61     private static final int DIALOG_NETWORK_SELECTION = 100;
62     private static final int DIALOG_NETWORK_LIST_LOAD = 200;
63 
64     private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
65     private List<OperatorInfo> mOperatorInfoList;
66     private OperatorInfo mOperatorInfo;
67 
68     private int mSubId;
69     private NetworkOperators mNetworkOperators;
70 
71     private ProgressDialog mProgressDialog;
NetworkSelectListPreference(Context context, AttributeSet attrs)72     public NetworkSelectListPreference(Context context, AttributeSet attrs) {
73         super(context, attrs);
74     }
75 
NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)76     public NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr,
77             int defStyleRes) {
78         super(context, attrs, defStyleAttr, defStyleRes);
79     }
80 
81     @Override
onClick()82     protected void onClick() {
83         loadNetworksList();
84     }
85 
86     private final Handler mHandler = new Handler() {
87         @Override
88         public void handleMessage(Message msg) {
89             AsyncResult ar;
90             switch (msg.what) {
91                 case EVENT_NETWORK_SCAN_COMPLETED:
92                     networksListLoaded((List<OperatorInfo>) msg.obj, msg.arg1);
93                     break;
94 
95                 case EVENT_NETWORK_SELECTION_DONE:
96                     if (DBG) logd("hideProgressPanel");
97                     try {
98                         dismissProgressBar();
99                     } catch (IllegalArgumentException e) {
100                     }
101                     setEnabled(true);
102 
103                     ar = (AsyncResult) msg.obj;
104                     if (ar.exception != null) {
105                         if (DBG) logd("manual network selection: failed!");
106                         mNetworkOperators.displayNetworkSelectionFailed(ar.exception);
107                     } else {
108                         if (DBG) {
109                             logd("manual network selection: succeeded!"
110                                     + getNetworkTitle(mOperatorInfo));
111                         }
112                         mNetworkOperators.displayNetworkSelectionSucceeded();
113                     }
114                     mNetworkOperators.getNetworkSelectionMode();
115                     break;
116             }
117 
118             return;
119         }
120     };
121 
122     INetworkQueryService mNetworkQueryService = null;
123     /**
124      * This implementation of INetworkQueryServiceCallback is used to receive
125      * callback notifications from the network query service.
126      */
127     private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
128 
129         /** place the message on the looper queue upon query completion. */
130         public void onQueryComplete(List<OperatorInfo> networkInfoArray, int status) {
131             if (DBG) logd("notifying message loop of query completion.");
132             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED,
133                     status, 0, networkInfoArray);
134             msg.sendToTarget();
135         }
136     };
137 
138     @Override
139     //implemented for DialogInterface.OnCancelListener
onCancel(DialogInterface dialog)140     public void onCancel(DialogInterface dialog) {
141         // request that the service stop the query with this callback object.
142         try {
143             if (mNetworkQueryService != null) {
144                 mNetworkQueryService.stopNetworkQuery(mCallback);
145             }
146             // If cancelled, we query NetworkSelectMode and update states of AutoSelect button.
147             mNetworkOperators.getNetworkSelectionMode();
148         } catch (RemoteException e) {
149             loge("onCancel: exception from stopNetworkQuery " + e);
150         }
151     }
152 
153     @Override
onDialogClosed(boolean positiveResult)154     protected void onDialogClosed(boolean positiveResult) {
155         super.onDialogClosed(positiveResult);
156 
157         // If dismissed, we query NetworkSelectMode and update states of AutoSelect button.
158         if (!positiveResult) {
159             mNetworkOperators.getNetworkSelectionMode();
160         }
161     }
162 
163     /**
164      * Return normalized carrier name given network info.
165      *
166      * @param ni is network information in OperatorInfo type.
167      */
getNormalizedCarrierName(OperatorInfo ni)168     public String getNormalizedCarrierName(OperatorInfo ni) {
169         if (ni != null) {
170             return ni.getOperatorAlphaLong() + " (" + ni.getOperatorNumeric() + ")";
171         }
172         return null;
173     }
174 
175     // This method is provided besides initialize() because bind to network query service
176     // may be binded after initialize(). In that case this method needs to be called explicitly
177     // to set mNetworkQueryService. Otherwise mNetworkQueryService will remain null.
setNetworkQueryService(INetworkQueryService queryService)178     public void setNetworkQueryService(INetworkQueryService queryService) {
179         mNetworkQueryService = queryService;
180     }
181 
182     // This initialize method needs to be called for this preference to work properly.
initialize(int subId, INetworkQueryService queryService, NetworkOperators networkOperators, ProgressDialog progressDialog)183     protected void initialize(int subId, INetworkQueryService queryService,
184             NetworkOperators networkOperators, ProgressDialog progressDialog) {
185         mSubId = subId;
186         mNetworkQueryService = queryService;
187         mNetworkOperators = networkOperators;
188         // This preference should share the same progressDialog with networkOperators category.
189         mProgressDialog = progressDialog;
190 
191         if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
192             mPhoneId = SubscriptionManager.getPhoneId(mSubId);
193         }
194 
195         TelephonyManager telephonyManager = (TelephonyManager)
196                 getContext().getSystemService(Context.TELEPHONY_SERVICE);
197 
198         setSummary(telephonyManager.getNetworkOperatorName());
199 
200         setOnPreferenceChangeListener(this);
201     }
202 
203     @Override
onPrepareForRemoval()204     protected void onPrepareForRemoval() {
205         destroy();
206         super.onPrepareForRemoval();
207     }
208 
destroy()209     private void destroy() {
210         try {
211             dismissProgressBar();
212         } catch (IllegalArgumentException e) {
213             loge("onDestroy: exception from dismissProgressBar " + e);
214         }
215 
216         try {
217             if (mNetworkQueryService != null) {
218                 // used to un-register callback
219                 mNetworkQueryService.unregisterCallback(mCallback);
220             }
221         } catch (RemoteException e) {
222             loge("onDestroy: exception from unregisterCallback " + e);
223         }
224     }
225 
displayEmptyNetworkList()226     private void displayEmptyNetworkList() {
227         String status = getContext().getResources().getString(R.string.empty_networks_list);
228 
229         final PhoneGlobals app = PhoneGlobals.getInstance();
230         app.notificationMgr.postTransientNotification(
231                 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
232     }
233 
displayNetworkSelectionInProgress()234     private void displayNetworkSelectionInProgress() {
235         showProgressBar(DIALOG_NETWORK_SELECTION);
236     }
237 
displayNetworkQueryFailed(int error)238     private void displayNetworkQueryFailed(int error) {
239         String status = getContext().getResources().getString(R.string.network_query_error);
240 
241         try {
242             dismissProgressBar();
243         } catch (IllegalArgumentException e1) {
244             // do nothing
245         }
246 
247         final PhoneGlobals app = PhoneGlobals.getInstance();
248         app.notificationMgr.postTransientNotification(
249                 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
250     }
251 
loadNetworksList()252     private void loadNetworksList() {
253         if (DBG) logd("load networks list...");
254 
255         showProgressBar(DIALOG_NETWORK_LIST_LOAD);
256 
257         // delegate query request to the service.
258         try {
259             if (mNetworkQueryService != null) {
260                 mNetworkQueryService.startNetworkQuery(mCallback, mPhoneId);
261             } else {
262                 displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION);
263             }
264         } catch (RemoteException e) {
265             loge("loadNetworksList: exception from startNetworkQuery " + e);
266             displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION);
267         }
268     }
269 
270     /**
271      * networksListLoaded has been rewritten to take an array of
272      * OperatorInfo objects and a status field, instead of an
273      * AsyncResult.  Otherwise, the functionality which takes the
274      * OperatorInfo array and creates a list of preferences from it,
275      * remains unchanged.
276      */
networksListLoaded(List<OperatorInfo> result, int status)277     private void networksListLoaded(List<OperatorInfo> result, int status) {
278         if (DBG) logd("networks list loaded");
279 
280         // used to un-register callback
281         try {
282             if (mNetworkQueryService != null) {
283                 mNetworkQueryService.unregisterCallback(mCallback);
284             }
285         } catch (RemoteException e) {
286             loge("networksListLoaded: exception from unregisterCallback " + e);
287         }
288 
289         // update the state of the preferences.
290         if (DBG) logd("hideProgressPanel");
291 
292         // Always try to dismiss the dialog because activity may
293         // be moved to background after dialog is shown.
294         try {
295             dismissProgressBar();
296         } catch (IllegalArgumentException e) {
297             // It's not a error in following scenario, we just ignore it.
298             // "Load list" dialog will not show, if NetworkQueryService is
299             // connected after this activity is moved to background.
300             loge("Fail to dismiss network load list dialog " + e);
301         }
302 
303         setEnabled(true);
304         clearList();
305 
306         if (status != NetworkQueryService.QUERY_OK) {
307             if (DBG) logd("error while querying available networks");
308             displayNetworkQueryFailed(status);
309         } else {
310             if (result != null) {
311                 // create a preference for each item in the list.
312                 // just use the operator name instead of the mildly
313                 // confusing mcc/mnc.
314                 mOperatorInfoList = result;
315                 CharSequence[] networkEntries = new CharSequence[result.size()];
316                 CharSequence[] networkEntryValues = new CharSequence[result.size()];
317                 for (int i = 0; i < mOperatorInfoList.size(); i++) {
318                     if (mOperatorInfoList.get(i).getState() == OperatorInfo.State.FORBIDDEN) {
319                         networkEntries[i] = getNetworkTitle(mOperatorInfoList.get(i))
320                             + " "
321                             + getContext().getResources().getString(R.string.forbidden_network);
322                     } else {
323                         networkEntries[i] = getNetworkTitle(mOperatorInfoList.get(i));
324                     }
325                     networkEntryValues[i] = Integer.toString(i + 2);
326                 }
327 
328                 setEntries(networkEntries);
329                 setEntryValues(networkEntryValues);
330 
331                 super.onClick();
332             } else {
333                 displayEmptyNetworkList();
334             }
335         }
336     }
337 
338     /**
339      * Returns the title of the network obtained in the manual search.
340      *
341      * @param ni contains the information of the network.
342      *
343      * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
344      * else MCCMNC string.
345      */
getNetworkTitle(OperatorInfo ni)346     private String getNetworkTitle(OperatorInfo ni) {
347         if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) {
348             return ni.getOperatorAlphaLong();
349         } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) {
350             return ni.getOperatorAlphaShort();
351         } else {
352             BidiFormatter bidiFormatter = BidiFormatter.getInstance();
353             return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR);
354         }
355     }
356 
clearList()357     private void clearList() {
358         if (mOperatorInfoList != null) {
359             mOperatorInfoList.clear();
360         }
361     }
362 
dismissProgressBar()363     private void dismissProgressBar() {
364         if (mProgressDialog != null && mProgressDialog.isShowing()) {
365             mProgressDialog.dismiss();
366         }
367     }
368 
showProgressBar(int id)369     private void showProgressBar(int id) {
370         if (mProgressDialog == null) {
371             mProgressDialog = new ProgressDialog(getContext());
372         } else {
373             // Dismiss progress bar if it's showing now.
374             dismissProgressBar();
375         }
376 
377         if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD)) {
378             switch (id) {
379                 case DIALOG_NETWORK_SELECTION:
380                     final String networkSelectMsg = getContext().getResources()
381                             .getString(R.string.register_on_network,
382                                     getNetworkTitle(mOperatorInfo));
383                     mProgressDialog.setMessage(networkSelectMsg);
384                     mProgressDialog.setCanceledOnTouchOutside(false);
385                     mProgressDialog.setCancelable(false);
386                     mProgressDialog.setIndeterminate(true);
387                     break;
388                 case DIALOG_NETWORK_LIST_LOAD:
389                     mProgressDialog.setMessage(
390                             getContext().getResources().getString(R.string.load_networks_progress));
391                     mProgressDialog.setCanceledOnTouchOutside(false);
392                     mProgressDialog.setCancelable(true);
393                     mProgressDialog.setIndeterminate(false);
394                     mProgressDialog.setOnCancelListener(this);
395                     break;
396                 default:
397             }
398             mProgressDialog.show();
399         }
400     }
401 
402     /**
403      * Implemented to support onPreferenceChangeListener to look for preference
404      * changes specifically on this button.
405      *
406      * @param preference is the preference to be changed, should be network select button.
407      * @param newValue should be the value of the selection as index of operators.
408      */
onPreferenceChange(Preference preference, Object newValue)409     public boolean onPreferenceChange(Preference preference, Object newValue) {
410         int operatorIndex = findIndexOfValue((String) newValue);
411         mOperatorInfo = mOperatorInfoList.get(operatorIndex);
412 
413         if (DBG) logd("selected network: " + getNetworkTitle(mOperatorInfo));
414 
415         Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
416         Phone phone = PhoneFactory.getPhone(mPhoneId);
417         if (phone != null) {
418             phone.selectNetworkManually(mOperatorInfo, true, msg);
419             displayNetworkSelectionInProgress();
420         } else {
421             loge("Error selecting network. phone is null.");
422         }
423 
424         return true;
425     }
426 
427     @Override
onSaveInstanceState()428     protected Parcelable onSaveInstanceState() {
429         final Parcelable superState = super.onSaveInstanceState();
430         if (isPersistent()) {
431             // No need to save instance state since it's persistent
432             return superState;
433         }
434 
435         final SavedState myState = new SavedState(superState);
436         myState.mDialogListEntries = getEntries();
437         myState.mDialogListEntryValues = getEntryValues();
438         myState.mOperatorInfoList = mOperatorInfoList;
439         return myState;
440     }
441 
442     @Override
onRestoreInstanceState(Parcelable state)443     protected void onRestoreInstanceState(Parcelable state) {
444         if (state == null || !state.getClass().equals(SavedState.class)) {
445             // Didn't save state for us in onSaveInstanceState
446             super.onRestoreInstanceState(state);
447             return;
448         }
449 
450         SavedState myState = (SavedState) state;
451 
452         if (getEntries() == null && myState.mDialogListEntries != null) {
453             setEntries(myState.mDialogListEntries);
454         }
455         if (getEntryValues() == null && myState.mDialogListEntryValues != null) {
456             setEntryValues(myState.mDialogListEntryValues);
457         }
458         if (mOperatorInfoList == null && myState.mOperatorInfoList != null) {
459             mOperatorInfoList = myState.mOperatorInfoList;
460         }
461 
462         super.onRestoreInstanceState(myState.getSuperState());
463     }
464 
465     /**
466      *  We save entries, entryValues and operatorInfoList into bundle.
467      *  At onCreate of fragment, dialog will be restored if it was open. In this case,
468      *  we need to restore entries, entryValues and operatorInfoList. Without those information,
469      *  onPreferenceChange will fail if user select network from the dialog.
470      */
471     private static class SavedState extends BaseSavedState {
472         CharSequence[] mDialogListEntries;
473         CharSequence[] mDialogListEntryValues;
474         List<OperatorInfo> mOperatorInfoList;
475 
SavedState(Parcel source)476         SavedState(Parcel source) {
477             super(source);
478             final ClassLoader boot = Object.class.getClassLoader();
479             mDialogListEntries = source.readCharSequenceArray();
480             mDialogListEntryValues = source.readCharSequenceArray();
481             mOperatorInfoList = source.readParcelableList(mOperatorInfoList, boot);
482         }
483 
484         @Override
writeToParcel(Parcel dest, int flags)485         public void writeToParcel(Parcel dest, int flags) {
486             super.writeToParcel(dest, flags);
487             dest.writeCharSequenceArray(mDialogListEntries);
488             dest.writeCharSequenceArray(mDialogListEntryValues);
489             dest.writeParcelableList(mOperatorInfoList, flags);
490         }
491 
SavedState(Parcelable superState)492         SavedState(Parcelable superState) {
493             super(superState);
494         }
495 
496         public static final Parcelable.Creator<SavedState> CREATOR =
497                 new Parcelable.Creator<SavedState>() {
498                     public SavedState createFromParcel(Parcel in) {
499                         return new SavedState(in);
500                     }
501 
502                     public SavedState[] newArray(int size) {
503                         return new SavedState[size];
504                     }
505                 };
506     }
507 
logd(String msg)508     private void logd(String msg) {
509         Log.d(LOG_TAG, "[NetworksList] " + msg);
510     }
511 
loge(String msg)512     private void loge(String msg) {
513         Log.e(LOG_TAG, "[NetworksList] " + msg);
514     }
515 }
516