1/*
2 * Copyright (C) 2017 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 */
16package com.android.car.settings.bluetooth;
17
18import android.bluetooth.BluetoothAdapter;
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.Bundle;
24import android.util.Log;
25import android.view.View;
26import android.support.v7.app.AppCompatActivity;
27import android.support.v7.widget.RecyclerView;
28import android.support.v4.widget.SwipeRefreshLayout;
29import android.widget.ProgressBar;
30import android.widget.Switch;
31import android.widget.TextView;
32import android.widget.ViewSwitcher;
33
34import com.android.car.settings.common.BaseFragment;
35import com.android.car.settings.R;
36import com.android.car.view.PagedListView;
37
38import com.android.settingslib.bluetooth.BluetoothCallback;
39import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
40import com.android.settingslib.bluetooth.CachedBluetoothDevice;
41import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
42import com.android.settingslib.bluetooth.LocalBluetoothManager;
43
44/**
45 * Hosts Bluetooth related preferences.
46 */
47public class BluetoothSettingsFragment extends BaseFragment implements BluetoothCallback {
48    private static final String TAG = "BluetoothSettingsFragment";
49
50    private SwipeRefreshLayout mSwipeRefreshLayout;
51    private Switch mBluetoothSwitch;
52    private ProgressBar mProgressBar;
53    private PagedListView mDeviceListView;
54    private ViewSwitcher mViewSwitcher;
55    private TextView mMessageView;
56    private BluetoothDeviceListAdapter mDeviceAdapter;
57    private LocalBluetoothAdapter mLocalAdapter;
58    private LocalBluetoothManager mLocalManager;
59
60    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
61        @Override
62        public void onReceive(Context context, Intent intent) {
63            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
64                setProgressBarVisible(true);
65                mBluetoothSwitch.setChecked(true);
66                if (mViewSwitcher.getCurrentView() != mSwipeRefreshLayout) {
67                    mViewSwitcher.showPrevious();
68                }
69            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
70                setProgressBarVisible(false);
71            }
72        }
73    };
74
75    public static BluetoothSettingsFragment getInstance() {
76        BluetoothSettingsFragment bluetoothSettingsFragment = new BluetoothSettingsFragment();
77        Bundle bundle = BaseFragment.getBundle();
78        bundle.putInt(EXTRA_TITLE_ID, R.string.bluetooth_settings);
79        bundle.putInt(EXTRA_LAYOUT, R.layout.bluetooth_list);
80        bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar_with_toggle);
81        bluetoothSettingsFragment.setArguments(bundle);
82        return bluetoothSettingsFragment;
83    }
84
85    @Override
86    public void onActivityCreated(Bundle savedInstanceState) {
87        super.onActivityCreated(savedInstanceState);
88        mBluetoothSwitch = getActivity().findViewById(R.id.toggle_switch);
89        mSwipeRefreshLayout = getActivity().findViewById(R.id.swiperefresh);
90        mSwipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
91        mSwipeRefreshLayout.setOnRefreshListener(
92                new SwipeRefreshLayout.OnRefreshListener() {
93                    @Override
94                    public void onRefresh() {
95                        mSwipeRefreshLayout.setRefreshing(false);
96                        if (mLocalAdapter.isDiscovering()) {
97                            mLocalAdapter.cancelDiscovery();
98                        }
99                        mDeviceAdapter.reset();
100                    }
101                }
102        );
103
104        mBluetoothSwitch.setOnCheckedChangeListener((v, isChecked) -> {
105                if (mBluetoothSwitch.isChecked()) {
106                    // bt scan was turned on at state listener, when state is on.
107                    mLocalAdapter.setBluetoothEnabled(true);
108                } else {
109                    mLocalAdapter.stopScanning();
110                    mLocalAdapter.setBluetoothEnabled(false);
111                }
112            });
113
114        mProgressBar = getView().findViewById(R.id.bt_search_progress);
115        mDeviceListView = getView().findViewById(R.id.list);
116        mViewSwitcher = getView().findViewById(R.id.view_switcher);
117        mMessageView = getView().findViewById(R.id.bt_message);
118
119        mLocalManager = LocalBluetoothManager.getInstance(getContext(), null /* listener */);
120        if (mLocalManager == null) {
121            Log.e(TAG, "Bluetooth is not supported on this device");
122            return;
123        }
124        mLocalAdapter = mLocalManager.getBluetoothAdapter();
125
126        // Set this to light mode, since the scroll bar buttons always appear
127        // on top of a dark scrim.
128        mDeviceListView.setDarkMode();
129        mDeviceAdapter = new BluetoothDeviceListAdapter(
130                getContext() , mLocalManager, mFragmentController);
131        mDeviceListView.setAdapter(mDeviceAdapter);
132    }
133
134    @Override
135    public void onStart() {
136        super.onStart();
137        if (mLocalManager == null) {
138            return;
139        }
140        IntentFilter filter = new IntentFilter();
141        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
142        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
143        getActivity().registerReceiver(mBroadcastReceiver, filter);
144
145        mLocalManager.setForegroundActivity(getActivity());
146        mLocalManager.getEventManager().registerCallback(this);
147        mBluetoothSwitch.setChecked(mLocalAdapter.isEnabled());
148        if (mLocalAdapter.isEnabled()) {
149            setProgressBarVisible(true);
150            mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
151            mLocalAdapter.startScanning(true);
152            if (mViewSwitcher.getCurrentView() != mSwipeRefreshLayout) {
153                mViewSwitcher.showPrevious();
154            }
155        } else {
156            setProgressBarVisible(false);
157            if (mViewSwitcher.getCurrentView() != mMessageView) {
158                mViewSwitcher.showNext();
159            }
160        }
161        mDeviceAdapter.start();
162    }
163
164    @Override
165    public void onStop() {
166        super.onStop();
167        if (mLocalManager == null) {
168            return;
169        }
170        getActivity().unregisterReceiver(mBroadcastReceiver);
171        mDeviceAdapter.stop();
172        mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
173        mLocalManager.setForegroundActivity(null);
174        mLocalAdapter.stopScanning();
175        mLocalManager.getEventManager().unregisterCallback(this);
176    }
177
178    @Override
179    public void onBluetoothStateChanged(int bluetoothState) {
180        switch (bluetoothState) {
181            case BluetoothAdapter.STATE_OFF:
182                setProgressBarVisible(false);
183                mBluetoothSwitch.setChecked(false);
184                if (mViewSwitcher.getCurrentView() != mMessageView) {
185                    mViewSwitcher.showNext();
186                }
187                break;
188            case BluetoothAdapter.STATE_ON:
189            case BluetoothAdapter.STATE_TURNING_ON:
190                setProgressBarVisible(true);
191                mBluetoothSwitch.setChecked(true);
192                if (mViewSwitcher.getCurrentView() != mSwipeRefreshLayout) {
193                        mViewSwitcher.showPrevious();
194                }
195                break;
196            case BluetoothAdapter.STATE_TURNING_OFF:
197                setProgressBarVisible(true);
198                break;
199        }
200    }
201
202    @Override
203    public void onScanningStateChanged(boolean started) {
204        if (!started) {
205            setProgressBarVisible(false);
206        }
207    }
208
209    @Override
210    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
211        // no-op
212    }
213
214    @Override
215    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
216        // no-op
217    }
218
219    @Override
220    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
221        // no-op
222    }
223
224    @Override
225    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
226        // no-op
227    }
228
229    private  void setProgressBarVisible(boolean visible) {
230        if (mProgressBar != null) {
231            mProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
232        }
233    }
234}
235