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