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.OperatorInfo; 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, OperatorInfo> 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<OperatorInfo>) 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<OperatorInfo> 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(OperatorInfo 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.getPhone(); 222 223 mNetworkList = (PreferenceGroup) getPreferenceScreen().findPreference(LIST_NETWORKS_KEY); 224 mNetworkMap = new HashMap<Preference, OperatorInfo>(); 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 final PhoneApp app = PhoneApp.getInstance(); 325 app.notificationMgr.postTransientNotification( 326 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 327 } 328 329 private void displayNetworkSelectionFailed(Throwable ex) { 330 String status; 331 332 if ((ex != null && ex instanceof CommandException) && 333 ((CommandException)ex).getCommandError() 334 == CommandException.Error.ILLEGAL_SIM_OR_ME) 335 { 336 status = getResources().getString(R.string.not_allowed); 337 } else { 338 status = getResources().getString(R.string.connect_later); 339 } 340 341 final PhoneApp app = PhoneApp.getInstance(); 342 app.notificationMgr.postTransientNotification( 343 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 344 } 345 346 private void displayNetworkSelectionSucceeded() { 347 String status = getResources().getString(R.string.registration_done); 348 349 final PhoneApp app = PhoneApp.getInstance(); 350 app.notificationMgr.postTransientNotification( 351 NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status); 352 353 mHandler.postDelayed(new Runnable() { 354 public void run() { 355 finish(); 356 } 357 }, 3000); 358 } 359 360 private void loadNetworksList() { 361 if (DBG) log("load networks list..."); 362 363 if (mIsForeground) { 364 showDialog(DIALOG_NETWORK_LIST_LOAD); 365 } 366 367 // delegate query request to the service. 368 try { 369 mNetworkQueryService.startNetworkQuery(mCallback); 370 } catch (RemoteException e) { 371 } 372 373 displayEmptyNetworkList(false); 374 } 375 376 /** 377 * networksListLoaded has been rewritten to take an array of 378 * OperatorInfo objects and a status field, instead of an 379 * AsyncResult. Otherwise, the functionality which takes the 380 * OperatorInfo array and creates a list of preferences from it, 381 * remains unchanged. 382 */ 383 private void networksListLoaded(List<OperatorInfo> result, int status) { 384 if (DBG) log("networks list loaded"); 385 386 // update the state of the preferences. 387 if (DBG) log("hideProgressPanel"); 388 389 if (mIsForeground) { 390 dismissDialog(DIALOG_NETWORK_LIST_LOAD); 391 } 392 393 getPreferenceScreen().setEnabled(true); 394 clearList(); 395 396 if (status != NetworkQueryService.QUERY_OK) { 397 if (DBG) log("error while querying available networks"); 398 displayNetworkQueryFailed(status); 399 displayEmptyNetworkList(true); 400 } else { 401 if (result != null){ 402 displayEmptyNetworkList(false); 403 404 // create a preference for each item in the list. 405 // just use the operator name instead of the mildly 406 // confusing mcc/mnc. 407 for (OperatorInfo ni : result) { 408 Preference carrier = new Preference(this, null); 409 carrier.setTitle(ni.getOperatorAlphaLong()); 410 carrier.setPersistent(false); 411 mNetworkList.addPreference(carrier); 412 mNetworkMap.put(carrier, ni); 413 414 if (DBG) log(" " + ni); 415 } 416 417 } else { 418 displayEmptyNetworkList(true); 419 } 420 } 421 } 422 423 private void clearList() { 424 for (Preference p : mNetworkMap.keySet()) { 425 mNetworkList.removePreference(p); 426 } 427 mNetworkMap.clear(); 428 } 429 430 private void selectNetworkAutomatic() { 431 if (DBG) log("select network automatically..."); 432 if (mIsForeground) { 433 showDialog(DIALOG_NETWORK_AUTO_SELECT); 434 } 435 436 Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE); 437 mPhone.setNetworkSelectionModeAutomatic(msg); 438 } 439 440 private void log(String msg) { 441 Log.d(LOG_TAG, "[NetworksList] " + msg); 442 } 443} 444