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 17package com.android.phone; 18 19import android.app.Dialog; 20import android.app.ProgressDialog; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.os.AsyncResult; 27import android.os.Bundle; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.Message; 31import android.os.RemoteException; 32import android.preference.Preference; 33import android.preference.PreferenceActivity; 34import android.preference.PreferenceGroup; 35import android.preference.PreferenceScreen; 36import android.util.Log; 37 38import com.android.internal.telephony.CommandException; 39import com.android.internal.telephony.Phone; 40import com.android.internal.telephony.gsm.NetworkInfo; 41 42import java.util.HashMap; 43import java.util.List; 44 45/** 46 * "Networks" settings UI for the Phone app. 47 */ 48public 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