1/*
2 * Copyright (C) 2010 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.settings.bluetooth;
18
19import android.bluetooth.BluetoothAdapter;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.os.Handler;
25import android.os.Message;
26import android.provider.Settings;
27import android.util.Log;
28import android.widget.Switch;
29import android.widget.Toast;
30
31import com.android.internal.logging.MetricsLogger;
32import com.android.internal.logging.MetricsProto.MetricsEvent;
33import com.android.settings.R;
34import com.android.settings.search.Index;
35import com.android.settings.widget.SwitchBar;
36import com.android.settingslib.WirelessUtils;
37import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
38import com.android.settingslib.bluetooth.LocalBluetoothManager;
39
40/**
41 * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
42 * preference. It turns on/off Bluetooth and ensures the summary of the
43 * preference reflects the current state.
44 */
45public final class BluetoothEnabler implements SwitchBar.OnSwitchChangeListener {
46    private Context mContext;
47    private Switch mSwitch;
48    private SwitchBar mSwitchBar;
49    private boolean mValidListener;
50    private final LocalBluetoothAdapter mLocalAdapter;
51    private final IntentFilter mIntentFilter;
52
53    private static final String EVENT_DATA_IS_BT_ON = "is_bluetooth_on";
54    private static final int EVENT_UPDATE_INDEX = 0;
55
56    private Handler mHandler = new Handler() {
57        @Override
58        public void handleMessage(Message msg) {
59            switch (msg.what) {
60                case EVENT_UPDATE_INDEX:
61                    final boolean isBluetoothOn = msg.getData().getBoolean(EVENT_DATA_IS_BT_ON);
62                    Index.getInstance(mContext).updateFromClassNameResource(
63                            BluetoothSettings.class.getName(), true, isBluetoothOn);
64                    break;
65            }
66        }
67    };
68
69    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
70        @Override
71        public void onReceive(Context context, Intent intent) {
72            // Broadcast receiver is always running on the UI thread here,
73            // so we don't need consider thread synchronization.
74            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
75            handleStateChanged(state);
76        }
77    };
78
79    public BluetoothEnabler(Context context, SwitchBar switchBar) {
80        mContext = context;
81        mSwitchBar = switchBar;
82        mSwitch = switchBar.getSwitch();
83        mValidListener = false;
84
85        LocalBluetoothManager manager = Utils.getLocalBtManager(context);
86        if (manager == null) {
87            // Bluetooth is not supported
88            mLocalAdapter = null;
89            mSwitch.setEnabled(false);
90        } else {
91            mLocalAdapter = manager.getBluetoothAdapter();
92        }
93        mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
94    }
95
96    public void setupSwitchBar() {
97        mSwitchBar.show();
98    }
99
100    public void teardownSwitchBar() {
101        mSwitchBar.hide();
102    }
103
104    public void resume(Context context) {
105        if (mLocalAdapter == null) {
106            mSwitch.setEnabled(false);
107            return;
108        }
109
110        if (mContext != context) {
111            mContext = context;
112        }
113
114        // Bluetooth state is not sticky, so set it manually
115        handleStateChanged(mLocalAdapter.getBluetoothState());
116
117        mSwitchBar.addOnSwitchChangeListener(this);
118        mContext.registerReceiver(mReceiver, mIntentFilter);
119        mValidListener = true;
120    }
121
122    public void pause() {
123        if (mLocalAdapter == null) {
124            return;
125        }
126
127        mSwitchBar.removeOnSwitchChangeListener(this);
128        mContext.unregisterReceiver(mReceiver);
129        mValidListener = false;
130    }
131
132    void handleStateChanged(int state) {
133        switch (state) {
134            case BluetoothAdapter.STATE_TURNING_ON:
135                mSwitch.setEnabled(false);
136                break;
137            case BluetoothAdapter.STATE_ON:
138                setChecked(true);
139                mSwitch.setEnabled(true);
140                updateSearchIndex(true);
141                break;
142            case BluetoothAdapter.STATE_TURNING_OFF:
143                mSwitch.setEnabled(false);
144                break;
145            case BluetoothAdapter.STATE_OFF:
146                setChecked(false);
147                mSwitch.setEnabled(true);
148                updateSearchIndex(false);
149                break;
150            default:
151                setChecked(false);
152                mSwitch.setEnabled(true);
153                updateSearchIndex(false);
154        }
155    }
156
157    private void setChecked(boolean isChecked) {
158        if (isChecked != mSwitch.isChecked()) {
159            // set listener to null, so onCheckedChanged won't be called
160            // if the checked status on Switch isn't changed by user click
161            if (mValidListener) {
162                mSwitchBar.removeOnSwitchChangeListener(this);
163            }
164            mSwitch.setChecked(isChecked);
165            if (mValidListener) {
166                mSwitchBar.addOnSwitchChangeListener(this);
167            }
168        }
169    }
170
171    private void updateSearchIndex(boolean isBluetoothOn) {
172        mHandler.removeMessages(EVENT_UPDATE_INDEX);
173
174        Message msg = new Message();
175        msg.what = EVENT_UPDATE_INDEX;
176        msg.getData().putBoolean(EVENT_DATA_IS_BT_ON, isBluetoothOn);
177        mHandler.sendMessage(msg);
178    }
179
180    @Override
181    public void onSwitchChanged(Switch switchView, boolean isChecked) {
182        // Show toast message if Bluetooth is not allowed in airplane mode
183        if (isChecked &&
184                !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
185            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
186            // Reset switch to off
187            switchView.setChecked(false);
188        }
189
190        MetricsLogger.action(mContext, MetricsEvent.ACTION_BLUETOOTH_TOGGLE, isChecked);
191
192        if (mLocalAdapter != null) {
193            boolean status = mLocalAdapter.setBluetoothEnabled(isChecked);
194            // If we cannot toggle it ON then reset the UI assets:
195            // a) The switch should be OFF but it should still be togglable (enabled = True)
196            // b) The switch bar should have OFF text.
197            if (isChecked && !status) {
198                switchView.setChecked(false);
199                mSwitch.setEnabled(true);
200                mSwitchBar.setTextViewLabel(false);
201                return;
202            }
203        }
204        mSwitch.setEnabled(false);
205    }
206}
207