1/*
2 * Copyright (C) 2014 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.systemui.qs.tiles;
18
19import android.bluetooth.BluetoothDevice;
20import android.bluetooth.BluetoothProfile;
21import android.content.Context;
22import android.content.Intent;
23import android.provider.Settings;
24import android.text.TextUtils;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.Button;
28import android.widget.Switch;
29
30import com.android.internal.logging.MetricsLogger;
31import com.android.internal.logging.MetricsProto.MetricsEvent;
32import com.android.settingslib.bluetooth.CachedBluetoothDevice;
33import com.android.systemui.R;
34import com.android.systemui.qs.QSDetailItems;
35import com.android.systemui.qs.QSDetailItems.Item;
36import com.android.systemui.qs.QSTile;
37import com.android.systemui.statusbar.policy.BluetoothController;
38
39import java.util.ArrayList;
40import java.util.Collection;
41
42/** Quick settings tile: Bluetooth **/
43public class BluetoothTile extends QSTile<QSTile.BooleanState>  {
44    private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
45
46    private final BluetoothController mController;
47    private final BluetoothDetailAdapter mDetailAdapter;
48
49    public BluetoothTile(Host host) {
50        super(host);
51        mController = host.getBluetoothController();
52        mDetailAdapter = new BluetoothDetailAdapter();
53    }
54
55    @Override
56    public DetailAdapter getDetailAdapter() {
57        return mDetailAdapter;
58    }
59
60    @Override
61    public BooleanState newTileState() {
62        return new BooleanState();
63    }
64
65    @Override
66    public void setListening(boolean listening) {
67        if (listening) {
68            mController.addStateChangedCallback(mCallback);
69        } else {
70            mController.removeStateChangedCallback(mCallback);
71        }
72    }
73
74    @Override
75    protected void handleSecondaryClick() {
76        // Secondary clicks are header clicks, just toggle.
77        final boolean isEnabled = (Boolean)mState.value;
78        MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
79        mController.setBluetoothEnabled(!isEnabled);
80    }
81
82    @Override
83    public Intent getLongClickIntent() {
84        return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
85    }
86
87    @Override
88    protected void handleClick() {
89        if (!mController.canConfigBluetooth()) {
90            mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
91            return;
92        }
93        if (!mState.value) {
94            mState.value = true;
95            mController.setBluetoothEnabled(true);
96        }
97        showDetail(true);
98    }
99
100    @Override
101    public CharSequence getTileLabel() {
102        return mContext.getString(R.string.quick_settings_bluetooth_label);
103    }
104
105    @Override
106    protected void handleUpdateState(BooleanState state, Object arg) {
107        final boolean enabled = mController.isBluetoothEnabled();
108        final boolean connected = mController.isBluetoothConnected();
109        final boolean connecting = mController.isBluetoothConnecting();
110        state.value = enabled;
111        state.autoMirrorDrawable = false;
112        state.minimalContentDescription =
113                mContext.getString(R.string.accessibility_quick_settings_bluetooth);
114        if (enabled) {
115            state.label = null;
116            if (connected) {
117                state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
118                state.label = mController.getLastDeviceName();
119                state.contentDescription = mContext.getString(
120                        R.string.accessibility_bluetooth_name, state.label);
121                state.minimalContentDescription = state.minimalContentDescription + ","
122                        + state.contentDescription;
123            } else if (connecting) {
124                state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connecting);
125                state.contentDescription = mContext.getString(
126                        R.string.accessibility_quick_settings_bluetooth_connecting);
127                state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
128                state.minimalContentDescription = state.minimalContentDescription + ","
129                        + state.contentDescription;
130            } else {
131                state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
132                state.contentDescription = mContext.getString(
133                        R.string.accessibility_quick_settings_bluetooth_on) + ","
134                        + mContext.getString(R.string.accessibility_not_connected);
135                state.minimalContentDescription = state.minimalContentDescription + ","
136                        + mContext.getString(R.string.accessibility_not_connected);
137            }
138            if (TextUtils.isEmpty(state.label)) {
139                state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
140            }
141        } else {
142            state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_off);
143            state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
144            state.contentDescription = mContext.getString(
145                    R.string.accessibility_quick_settings_bluetooth_off);
146        }
147
148        CharSequence bluetoothName = state.label;
149        if (connected) {
150            bluetoothName = state.dualLabelContentDescription = mContext.getString(
151                    R.string.accessibility_bluetooth_name, state.label);
152        }
153        state.dualLabelContentDescription = bluetoothName;
154        state.contentDescription = state.contentDescription + "," + mContext.getString(
155                R.string.accessibility_quick_settings_open_settings, getTileLabel());
156        state.expandedAccessibilityClassName = Button.class.getName();
157        state.minimalAccessibilityClassName = Switch.class.getName();
158    }
159
160    @Override
161    public int getMetricsCategory() {
162        return MetricsEvent.QS_BLUETOOTH;
163    }
164
165    @Override
166    protected String composeChangeAnnouncement() {
167        if (mState.value) {
168            return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on);
169        } else {
170            return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_off);
171        }
172    }
173
174    @Override
175    public boolean isAvailable() {
176        return mController.isBluetoothSupported();
177    }
178
179    private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
180        @Override
181        public void onBluetoothStateChange(boolean enabled) {
182            refreshState();
183        }
184
185        @Override
186        public void onBluetoothDevicesChanged() {
187            mUiHandler.post(new Runnable() {
188                @Override
189                public void run() {
190                    mDetailAdapter.updateItems();
191                }
192            });
193            refreshState();
194        }
195    };
196
197    private final class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
198        private QSDetailItems mItems;
199
200        @Override
201        public CharSequence getTitle() {
202            return mContext.getString(R.string.quick_settings_bluetooth_label);
203        }
204
205        @Override
206        public Boolean getToggleState() {
207            return mState.value;
208        }
209
210        @Override
211        public Intent getSettingsIntent() {
212            return BLUETOOTH_SETTINGS;
213        }
214
215        @Override
216        public void setToggleState(boolean state) {
217            MetricsLogger.action(mContext, MetricsEvent.QS_BLUETOOTH_TOGGLE, state);
218            mController.setBluetoothEnabled(state);
219            showDetail(false);
220        }
221
222        @Override
223        public int getMetricsCategory() {
224            return MetricsEvent.QS_BLUETOOTH_DETAILS;
225        }
226
227        @Override
228        public View createDetailView(Context context, View convertView, ViewGroup parent) {
229            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
230            mItems.setTagSuffix("Bluetooth");
231            mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
232                    R.string.quick_settings_bluetooth_detail_empty_text);
233            mItems.setCallback(this);
234            updateItems();
235            setItemsVisible(mState.value);
236            return mItems;
237        }
238
239        public void setItemsVisible(boolean visible) {
240            if (mItems == null) return;
241            mItems.setItemsVisible(visible);
242        }
243
244        private void updateItems() {
245            if (mItems == null) return;
246            ArrayList<Item> items = new ArrayList<Item>();
247            final Collection<CachedBluetoothDevice> devices = mController.getDevices();
248            if (devices != null) {
249                for (CachedBluetoothDevice device : devices) {
250                    if (device.getBondState() == BluetoothDevice.BOND_NONE) continue;
251                    final Item item = new Item();
252                    item.icon = R.drawable.ic_qs_bluetooth_on;
253                    item.line1 = device.getName();
254                    int state = device.getMaxConnectionState();
255                    if (state == BluetoothProfile.STATE_CONNECTED) {
256                        item.icon = R.drawable.ic_qs_bluetooth_connected;
257                        item.line2 = mContext.getString(R.string.quick_settings_connected);
258                        item.canDisconnect = true;
259                    } else if (state == BluetoothProfile.STATE_CONNECTING) {
260                        item.icon = R.drawable.ic_qs_bluetooth_connecting;
261                        item.line2 = mContext.getString(R.string.quick_settings_connecting);
262                    }
263                    item.tag = device;
264                    items.add(item);
265                }
266            }
267            mItems.setItems(items.toArray(new Item[items.size()]));
268        }
269
270        @Override
271        public void onDetailItemClick(Item item) {
272            if (item == null || item.tag == null) return;
273            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
274            if (device != null && device.getMaxConnectionState()
275                    == BluetoothProfile.STATE_DISCONNECTED) {
276                mController.connect(device);
277            }
278        }
279
280        @Override
281        public void onDetailItemDisconnect(Item item) {
282            if (item == null || item.tag == null) return;
283            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
284            if (device != null) {
285                mController.disconnect(device);
286            }
287        }
288    }
289}
290