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