1 /*
2  * Copyright (C) 2021 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 
17 package com.google.android.car.kitchensink.bluetooth;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothDevicePicker;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.Button;
32 import android.widget.TableLayout;
33 import android.widget.TableRow;
34 import android.widget.TextView;
35 import android.widget.Toast;
36 
37 import androidx.annotation.Nullable;
38 import androidx.fragment.app.Fragment;
39 
40 import com.google.android.car.kitchensink.R;
41 
42 import java.util.Set;
43 import java.util.concurrent.Executor;
44 
45 public class BluetoothDeviceFragment extends Fragment {
46     private static final String TAG = "CAR.BLUETOOTH.KS";
47     BluetoothAdapter mBluetoothAdapter;
48     BluetoothDevice mPickedDevice;
49     Executor mExecutor;
50     BluetoothDeviceTypeChecker mDeviceTypeChecker;
51 
52     TextView mPickedDeviceText;
53     Button mDevicePicker;
54     TableLayout mTableLayout;
55 
56     @Override
onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)57     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
58             @Nullable Bundle savedInstanceState) {
59         View v = inflater.inflate(R.layout.bluetooth_devices, container, false);
60         mDeviceTypeChecker = new BluetoothDeviceTypeChecker(getContext(), true);
61         mDevicePicker = v.findViewById(R.id.bluetooth_pick_device);
62         mTableLayout = v.findViewById(R.id.PairedDeviceTable);
63         mExecutor = new ThreadPerTaskExecutor();
64 
65         // Pick a bluetooth device
66         mDevicePicker.setOnClickListener(new View.OnClickListener() {
67             @Override
68             public void onClick(View view) {
69                 launchDevicePicker();
70             }
71         });
72 
73         return v;
74     }
75 
76     @Override
onResume()77     public void onResume() {
78         super.onResume();
79         checkAllDevices();
80     }
81 
launchDevicePicker()82     void launchDevicePicker() {
83         IntentFilter filter = new IntentFilter();
84         filter.addAction(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
85         getContext().registerReceiver(mPickerReceiver, filter);
86 
87         Intent intent = new Intent(BluetoothDevicePicker.ACTION_LAUNCH);
88         intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
89         getContext().startActivity(intent);
90     }
91 
checkAllDevices()92     void checkAllDevices() {
93         BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
94         if (bluetoothAdapter == null) {
95             Log.w(TAG, "Bluetooth Adapter not available");
96             return;
97         }
98         mTableLayout.removeAllViews();
99         Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
100         Context context = getContext();
101         for (BluetoothDevice device : bondedDevices) {
102             TableRow row = new TableRow(context);
103             TextView deviceName = new TextView(context);
104             deviceName.setText(device.getName());
105             TextView deviceType = new TextView(context);
106             deviceType.setText(Boolean.toString(mDeviceTypeChecker.isIapDevice(device)));
107             row.addView(deviceName);
108             row.addView(deviceType);
109             mTableLayout.addView(row);
110         }
111     }
112 
addDeviceToTable(BluetoothDevice device, String value)113     private void addDeviceToTable(BluetoothDevice device, String value) {
114         getActivity().runOnUiThread(new Runnable() {
115             public void run() {
116                 Context context = getContext();
117                 TableRow row = new TableRow(context);
118                 TextView deviceName = new TextView(context);
119                 TextView deviceValue = new TextView(context);
120                 deviceName.setText(device.getName());
121                 deviceValue.setText(value);
122                 row.addView(deviceName);
123                 row.addView(deviceValue);
124                 mTableLayout.addView(row);
125             }
126         });
127     }
128 
129     private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
130         @Override
131         public void onReceive(Context context, Intent intent) {
132             String action = intent.getAction();
133             Log.v(TAG, "mPickerReceiver got " + action);
134 
135             if (BluetoothDevicePicker.ACTION_DEVICE_SELECTED.equals(action)) {
136                 final BluetoothDevice device =
137                         intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
138                 Log.v(TAG, "mPickerReceiver got " + device);
139                 if (device == null) {
140                     Toast.makeText(getContext(), "No device selected", Toast.LENGTH_SHORT).show();
141                     return;
142                 }
143                 mExecutor.execute(new Runnable() {
144                     @Override
145                     public void run() {
146                         addDeviceToTable(device,
147                                 Boolean.toString(mDeviceTypeChecker.isIapDevice(device)));
148                         Log.w(TAG, "Is iAP" + mDeviceTypeChecker.isIapDevice(device));
149                     }
150                 });
151                 Log.w(TAG, "Dispatched");
152                 getContext().unregisterReceiver(mPickerReceiver);
153             }
154         }
155     };
156 
157     private class ThreadPerTaskExecutor implements Executor {
execute(Runnable r)158         public void execute(Runnable r) {
159             new Thread(r).start();
160         }
161     }
162 }
163