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