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