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