Home | History | Annotate | Download | only in phone
      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.Dialog;
     20 import android.app.ProgressDialog;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.ServiceConnection;
     26 import android.os.AsyncResult;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.IBinder;
     30 import android.os.Message;
     31 import android.os.RemoteException;
     32 import android.preference.Preference;
     33 import android.preference.PreferenceActivity;
     34 import android.preference.PreferenceGroup;
     35 import android.preference.PreferenceScreen;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 
     39 import com.android.internal.telephony.CommandException;
     40 import com.android.internal.telephony.Phone;
     41 import com.android.internal.telephony.OperatorInfo;
     42 
     43 import java.util.HashMap;
     44 import java.util.List;
     45 
     46 /**
     47  * "Networks" settings UI for the Phone app.
     48  */
     49 public class NetworkSetting extends PreferenceActivity
     50         implements DialogInterface.OnCancelListener {
     51 
     52     private static final String LOG_TAG = "phone";
     53     private static final boolean DBG = false;
     54 
     55     private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
     56     private static final int EVENT_NETWORK_SELECTION_DONE = 200;
     57     private static final int EVENT_AUTO_SELECT_DONE = 300;
     58 
     59     //dialog ids
     60     private static final int DIALOG_NETWORK_SELECTION = 100;
     61     private static final int DIALOG_NETWORK_LIST_LOAD = 200;
     62     private static final int DIALOG_NETWORK_AUTO_SELECT = 300;
     63 
     64     //String keys for preference lookup
     65     private static final String LIST_NETWORKS_KEY = "list_networks_key";
     66     private static final String BUTTON_SRCH_NETWRKS_KEY = "button_srch_netwrks_key";
     67     private static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key";
     68 
     69     //map of network controls to the network data.
     70     private HashMap<Preference, OperatorInfo> mNetworkMap;
     71 
     72     Phone mPhone;
     73     protected boolean mIsForeground = false;
     74 
     75     /** message for network selection */
     76     String mNetworkSelectMsg;
     77 
     78     //preference objects
     79     private PreferenceGroup mNetworkList;
     80     private Preference mSearchButton;
     81     private Preference mAutoSelect;
     82 
     83     private final Handler mHandler = new Handler() {
     84         @Override
     85         public void handleMessage(Message msg) {
     86             AsyncResult ar;
     87             switch (msg.what) {
     88                 case EVENT_NETWORK_SCAN_COMPLETED:
     89                     networksListLoaded ((List<OperatorInfo>) msg.obj, msg.arg1);
     90                     break;
     91 
     92                 case EVENT_NETWORK_SELECTION_DONE:
     93                     if (DBG) log("hideProgressPanel");
     94                     removeDialog(DIALOG_NETWORK_SELECTION);
     95                     getPreferenceScreen().setEnabled(true);
     96 
     97                     ar = (AsyncResult) msg.obj;
     98                     if (ar.exception != null) {
     99                         if (DBG) log("manual network selection: failed!");
    100                         displayNetworkSelectionFailed(ar.exception);
    101                     } else {
    102                         if (DBG) log("manual network selection: succeeded!");
    103                         displayNetworkSelectionSucceeded();
    104                     }
    105                     break;
    106                 case EVENT_AUTO_SELECT_DONE:
    107                     if (DBG) log("hideProgressPanel");
    108 
    109                     // Always try to dismiss the dialog because activity may
    110                     // be moved to background after dialog is shown.
    111                     try {
    112                         dismissDialog(DIALOG_NETWORK_AUTO_SELECT);
    113                     } catch (IllegalArgumentException e) {
    114                         // "auto select" is always trigged in foreground, so "auto select" dialog
    115                         //  should be shown when "auto select" is trigged. Should NOT get
    116                         // this exception, and Log it.
    117                         Log.w(LOG_TAG, "[NetworksList] Fail to dismiss auto select dialog", e);
    118                     }
    119                     getPreferenceScreen().setEnabled(true);
    120 
    121                     ar = (AsyncResult) msg.obj;
    122                     if (ar.exception != null) {
    123                         if (DBG) log("automatic network selection: failed!");
    124                         displayNetworkSelectionFailed(ar.exception);
    125                     } else {
    126                         if (DBG) log("automatic network selection: succeeded!");
    127                         displayNetworkSelectionSucceeded();
    128                     }
    129                     break;
    130             }
    131 
    132             return;
    133         }
    134     };
    135 
    136     /**
    137      * Service connection code for the NetworkQueryService.
    138      * Handles the work of binding to a local object so that we can make
    139      * the appropriate service calls.
    140      */
    141 
    142     /** Local service interface */
    143     private INetworkQueryService mNetworkQueryService = null;
    144 
    145     /** Service connection */
    146     private final ServiceConnection mNetworkQueryServiceConnection = new ServiceConnection() {
    147 
    148         /** Handle the task of binding the local object to the service */
    149         public void onServiceConnected(ComponentName className, IBinder service) {
    150             if (DBG) log("connection created, binding local service.");
    151             mNetworkQueryService = ((NetworkQueryService.LocalBinder) service).getService();
    152             // as soon as it is bound, run a query.
    153             loadNetworksList();
    154         }
    155 
    156         /** Handle the task of cleaning up the local binding */
    157         public void onServiceDisconnected(ComponentName className) {
    158             if (DBG) log("connection disconnected, cleaning local binding.");
    159             mNetworkQueryService = null;
    160         }
    161     };
    162 
    163     /**
    164      * This implementation of INetworkQueryServiceCallback is used to receive
    165      * callback notifications from the network query service.
    166      */
    167     private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
    168 
    169         /** place the message on the looper queue upon query completion. */
    170         public void onQueryComplete(List<OperatorInfo> networkInfoArray, int status) {
    171             if (DBG) log("notifying message loop of query completion.");
    172             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED,
    173                     status, 0, networkInfoArray);
    174             msg.sendToTarget();
    175         }
    176     };
    177 
    178     @Override
    179     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    180         boolean handled = false;
    181 
    182         if (preference == mSearchButton) {
    183             loadNetworksList();
    184             handled = true;
    185         } else if (preference == mAutoSelect) {
    186             selectNetworkAutomatic();
    187             handled = true;
    188         } else {
    189             Preference selectedCarrier = preference;
    190 
    191             String networkStr = selectedCarrier.getTitle().toString();
    192             if (DBG) log("selected network: " + networkStr);
    193 
    194             Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
    195             mPhone.selectNetworkManually(mNetworkMap.get(selectedCarrier), msg);
    196 
    197             displayNetworkSeletionInProgress(networkStr);
    198 
    199             handled = true;
    200         }
    201 
    202         return handled;
    203     }
    204 
    205     //implemented for DialogInterface.OnCancelListener
    206     public void onCancel(DialogInterface dialog) {
    207         // request that the service stop the query with this callback object.
    208         try {
    209             mNetworkQueryService.stopNetworkQuery(mCallback);
    210         } catch (RemoteException e) {
    211             throw new RuntimeException(e);
    212         }
    213         finish();
    214     }
    215 
    216     public String getNormalizedCarrierName(OperatorInfo ni) {
    217         if (ni != null) {
    218             return ni.getOperatorAlphaLong() + " (" + ni.getOperatorNumeric() + ")";
    219         }
    220         return null;
    221     }
    222 
    223     @Override
    224     protected void onCreate(Bundle icicle) {
    225         super.onCreate(icicle);
    226 
    227         addPreferencesFromResource(R.xml.carrier_select);
    228 
    229         mPhone = PhoneGlobals.getPhone();
    230 
    231         mNetworkList = (PreferenceGroup) getPreferenceScreen().findPreference(LIST_NETWORKS_KEY);
    232         mNetworkMap = new HashMap<Preference, OperatorInfo>();
    233 
    234         mSearchButton = getPreferenceScreen().findPreference(BUTTON_SRCH_NETWRKS_KEY);
    235         mAutoSelect = getPreferenceScreen().findPreference(BUTTON_AUTO_SELECT_KEY);
    236 
    237         // Start the Network Query service, and bind it.
    238         // The OS knows to start he service only once and keep the instance around (so
    239         // long as startService is called) until a stopservice request is made.  Since
    240         // we want this service to just stay in the background until it is killed, we
    241         // don't bother stopping it from our end.
    242         startService (new Intent(this, NetworkQueryService.class));
    243         bindService (new Intent(this, NetworkQueryService.class), mNetworkQueryServiceConnection,
    244                 Context.BIND_AUTO_CREATE);
    245     }
    246 
    247     @Override
    248     public void onResume() {
    249         super.onResume();
    250         mIsForeground = true;
    251     }
    252 
    253     @Override
    254     public void onPause() {
    255         super.onPause();
    256         mIsForeground = false;
    257     }
    258 
    259     /**
    260      * Override onDestroy() to unbind the query service, avoiding service
    261      * leak exceptions.
    262      */
    263     @Override
    264     protected void onDestroy() {
    265         // unbind the service.
    266         unbindService(mNetworkQueryServiceConnection);
    267 
    268         super.onDestroy();
    269     }
    270 
    271     @Override
    272     protected Dialog onCreateDialog(int id) {
    273 
    274         if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD) ||
    275                 (id == DIALOG_NETWORK_AUTO_SELECT)) {
    276             ProgressDialog dialog = new ProgressDialog(this);
    277             switch (id) {
    278                 case DIALOG_NETWORK_SELECTION:
    279                     // It would be more efficient to reuse this dialog by moving
    280                     // this setMessage() into onPreparedDialog() and NOT use
    281                     // removeDialog().  However, this is not possible since the
    282                     // message is rendered only 2 times in the ProgressDialog -
    283                     // after show() and before onCreate.
    284                     dialog.setMessage(mNetworkSelectMsg);
    285                     dialog.setCancelable(false);
    286                     dialog.setIndeterminate(true);
    287                     break;
    288                 case DIALOG_NETWORK_AUTO_SELECT:
    289                     dialog.setMessage(getResources().getString(R.string.register_automatically));
    290                     dialog.setCancelable(false);
    291                     dialog.setIndeterminate(true);
    292                     break;
    293                 case DIALOG_NETWORK_LIST_LOAD:
    294                 default:
    295                     // reinstate the cancelablity of the dialog.
    296                     dialog.setMessage(getResources().getString(R.string.load_networks_progress));
    297                     dialog.setCanceledOnTouchOutside(false);
    298                     dialog.setOnCancelListener(this);
    299                     break;
    300             }
    301             return dialog;
    302         }
    303         return null;
    304     }
    305 
    306     @Override
    307     protected void onPrepareDialog(int id, Dialog dialog) {
    308         if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD) ||
    309                 (id == DIALOG_NETWORK_AUTO_SELECT)) {
    310             // when the dialogs come up, we'll need to indicate that
    311             // we're in a busy state to dissallow further input.
    312             getPreferenceScreen().setEnabled(false);
    313         }
    314     }
    315 
    316     private void displayEmptyNetworkList(boolean flag) {
    317         mNetworkList.setTitle(flag ? R.string.empty_networks_list : R.string.label_available);
    318     }
    319 
    320     private void displayNetworkSeletionInProgress(String networkStr) {
    321         // TODO: use notification manager?
    322         mNetworkSelectMsg = getResources().getString(R.string.register_on_network, networkStr);
    323 
    324         if (mIsForeground) {
    325             showDialog(DIALOG_NETWORK_SELECTION);
    326         }
    327     }
    328 
    329     private void displayNetworkQueryFailed(int error) {
    330         String status = getResources().getString(R.string.network_query_error);
    331 
    332         final PhoneGlobals app = PhoneGlobals.getInstance();
    333         app.notificationMgr.postTransientNotification(
    334                 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
    335     }
    336 
    337     private void displayNetworkSelectionFailed(Throwable ex) {
    338         String status;
    339 
    340         if ((ex != null && ex instanceof CommandException) &&
    341                 ((CommandException)ex).getCommandError()
    342                   == CommandException.Error.ILLEGAL_SIM_OR_ME)
    343         {
    344             status = getResources().getString(R.string.not_allowed);
    345         } else {
    346             status = getResources().getString(R.string.connect_later);
    347         }
    348 
    349         final PhoneGlobals app = PhoneGlobals.getInstance();
    350         app.notificationMgr.postTransientNotification(
    351                 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
    352     }
    353 
    354     private void displayNetworkSelectionSucceeded() {
    355         String status = getResources().getString(R.string.registration_done);
    356 
    357         final PhoneGlobals app = PhoneGlobals.getInstance();
    358         app.notificationMgr.postTransientNotification(
    359                 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
    360 
    361         mHandler.postDelayed(new Runnable() {
    362             public void run() {
    363                 finish();
    364             }
    365         }, 3000);
    366     }
    367 
    368     private void loadNetworksList() {
    369         if (DBG) log("load networks list...");
    370 
    371         if (mIsForeground) {
    372             showDialog(DIALOG_NETWORK_LIST_LOAD);
    373         }
    374 
    375         // delegate query request to the service.
    376         try {
    377             mNetworkQueryService.startNetworkQuery(mCallback);
    378         } catch (RemoteException e) {
    379         }
    380 
    381         displayEmptyNetworkList(false);
    382     }
    383 
    384     /**
    385      * networksListLoaded has been rewritten to take an array of
    386      * OperatorInfo objects and a status field, instead of an
    387      * AsyncResult.  Otherwise, the functionality which takes the
    388      * OperatorInfo array and creates a list of preferences from it,
    389      * remains unchanged.
    390      */
    391     private void networksListLoaded(List<OperatorInfo> result, int status) {
    392         if (DBG) log("networks list loaded");
    393 
    394         // update the state of the preferences.
    395         if (DBG) log("hideProgressPanel");
    396 
    397 
    398         // Always try to dismiss the dialog because activity may
    399         // be moved to background after dialog is shown.
    400         try {
    401             dismissDialog(DIALOG_NETWORK_LIST_LOAD);
    402         } catch (IllegalArgumentException e) {
    403             // It's not a error in following scenario, we just ignore it.
    404             // "Load list" dialog will not show, if NetworkQueryService is
    405             // connected after this activity is moved to background.
    406             if (DBG) log("Fail to dismiss network load list dialog");
    407         }
    408 
    409         getPreferenceScreen().setEnabled(true);
    410         clearList();
    411 
    412         if (status != NetworkQueryService.QUERY_OK) {
    413             if (DBG) log("error while querying available networks");
    414             displayNetworkQueryFailed(status);
    415             displayEmptyNetworkList(true);
    416         } else {
    417             if (result != null){
    418                 displayEmptyNetworkList(false);
    419 
    420                 // create a preference for each item in the list.
    421                 // just use the operator name instead of the mildly
    422                 // confusing mcc/mnc.
    423                 for (OperatorInfo ni : result) {
    424                     Preference carrier = new Preference(this, null);
    425                     carrier.setTitle(getNetworkTitle(ni));
    426                     carrier.setPersistent(false);
    427                     mNetworkList.addPreference(carrier);
    428                     mNetworkMap.put(carrier, ni);
    429 
    430                     if (DBG) log("  " + ni);
    431                 }
    432 
    433             } else {
    434                 displayEmptyNetworkList(true);
    435             }
    436         }
    437     }
    438 
    439     /**
    440      * Returns the title of the network obtained in the manual search.
    441      *
    442      * @param OperatorInfo contains the information of the network.
    443      *
    444      * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
    445      * else MCCMNC string.
    446      */
    447 
    448     private String getNetworkTitle(OperatorInfo ni) {
    449         if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) {
    450             return ni.getOperatorAlphaLong();
    451         } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) {
    452             return ni.getOperatorAlphaShort();
    453         } else {
    454             return ni.getOperatorNumeric();
    455         }
    456     }
    457 
    458     private void clearList() {
    459         for (Preference p : mNetworkMap.keySet()) {
    460             mNetworkList.removePreference(p);
    461         }
    462         mNetworkMap.clear();
    463     }
    464 
    465     private void selectNetworkAutomatic() {
    466         if (DBG) log("select network automatically...");
    467         if (mIsForeground) {
    468             showDialog(DIALOG_NETWORK_AUTO_SELECT);
    469         }
    470 
    471         Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE);
    472         mPhone.setNetworkSelectionModeAutomatic(msg);
    473     }
    474 
    475     private void log(String msg) {
    476         Log.d(LOG_TAG, "[NetworksList] " + msg);
    477     }
    478 }
    479