DeviceDiscoveryService.java revision d44f9334ed8b4afc08f70099c46301525b1f8d71
16ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla/* 26ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Copyright (C) 2013 The Android Open Source Project 36ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 46ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Licensed under the Apache License, Version 2.0 (the "License"); 56ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * you may not use this file except in compliance with the License. 66ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * You may obtain a copy of the License at 76ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 86ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * http://www.apache.org/licenses/LICENSE-2.0 96ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Unless required by applicable law or agreed to in writing, software 116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * distributed under the License is distributed on an "AS IS" BASIS, 126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * See the License for the specific language governing permissions and 146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * limitations under the License. 156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla */ 166ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslapackage com.android.companiondevicemanager; 186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayName; 206ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport static android.companion.BluetoothLEDeviceFilter.nullsafe; 216ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 226ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.annotation.NonNull; 236ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.annotation.Nullable; 246ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.app.PendingIntent; 256ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.app.Service; 266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothAdapter; 276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothDevice; 286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothManager; 296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.BluetoothLeScanner; 306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanCallback; 316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanFilter; 326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanResult; 336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanSettings; 346ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.AssociationRequest; 356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.BluetoothDeviceFilterUtils; 366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.BluetoothLEDeviceFilter; 376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.ICompanionDeviceManagerService; 386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.IOnAssociateCallback; 396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.BroadcastReceiver; 406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.Context; 416ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.Intent; 426ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.IntentFilter; 436ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.graphics.Color; 446ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.graphics.PorterDuff; 456ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.graphics.drawable.Drawable; 466ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.os.IBinder; 476ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.os.RemoteException; 486ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.util.Log; 496ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.view.View; 506ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.view.ViewGroup; 516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.widget.ArrayAdapter; 526ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.widget.TextView; 536ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 546ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport java.util.ArrayList; 556ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport java.util.Collections; 566ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport java.util.List; 576ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslapublic class DeviceDiscoveryService extends Service { 596ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 606ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private static final boolean DEBUG = false; 616ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private static final String LOG_TAG = "DeviceDiscoveryService"; 626ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 636ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla static DeviceDiscoveryService sInstance; 646ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 656ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private BluetoothAdapter mBluetoothAdapter; 666ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private BluetoothLEDeviceFilter mFilter; 676ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private ScanFilter mScanFilter; 686ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private ScanSettings mDefaultScanSettings = new ScanSettings.Builder().build(); 69d44f9334ed8b4afc08f70099c46301525b1f8d71Eugene Susla AssociationRequest<?> mRequest; 706ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla List<BluetoothDevice> mDevicesFound; 716ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla BluetoothDevice mSelectedDevice; 726ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla DevicesAdapter mDevicesAdapter; 736ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla IOnAssociateCallback mCallback; 746ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla String mCallingPackage; 756ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 766ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private final ICompanionDeviceManagerService mBinder = 776ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla new ICompanionDeviceManagerService.Stub() { 786ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 796ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void startDiscovery(AssociationRequest request, 806ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla IOnAssociateCallback callback, 816ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla String callingPackage) throws RemoteException { 826ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) { 836ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Log.i(LOG_TAG, 846ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla "startDiscovery() called with: filter = [" + request + "], callback = [" 856ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla + callback + "]"); 866ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 876ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mCallback = callback; 886ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mCallingPackage = callingPackage; 896ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla DeviceDiscoveryService.this.startDiscovery(request); 906ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 916ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }; 926ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 936ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private final ScanCallback mBLEScanCallback = new ScanCallback() { 946ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 956ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void onScanResult(int callbackType, ScanResult result) { 966ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final BluetoothDevice device = result.getDevice(); 976ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) { 986ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onDeviceLost(device); 996ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } else { 1006ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onDeviceFound(device); 1016ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1026ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1036ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }; 1046ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1056ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private BluetoothLeScanner mBLEScanner; 1066ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1076ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private BroadcastReceiver mBluetoothDeviceFoundBroadcastReceiver = new BroadcastReceiver() { 1086ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1096ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void onReceive(Context context, Intent intent) { 1106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final BluetoothDevice device = intent.getParcelableExtra( 1116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla BluetoothDevice.EXTRA_DEVICE); 1126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (!mFilter.matches(device)) return; // ignore device 1136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { 1156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onDeviceFound(device); 1166ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } else { 1176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onDeviceLost(device); 1186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1206ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }; 1216ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1226ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1236ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public IBinder onBind(Intent intent) { 1246ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) Log.i(LOG_TAG, "onBind(" + intent + ")"); 1256ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return mBinder.asBinder(); 1266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void onCreate() { 1306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla super.onCreate(); 1316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) Log.i(LOG_TAG, "onCreate()"); 1336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1346ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter(); 1356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); 1366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound = new ArrayList<>(); 1386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter = new DevicesAdapter(); 1396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla sInstance = this; 1416ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1426ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1436ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void startDiscovery(AssociationRequest<?> request) { 1446ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla //TODO support other protocols as well 145d44f9334ed8b4afc08f70099c46301525b1f8d71Eugene Susla mRequest = request; 1466ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mFilter = nullsafe((BluetoothLEDeviceFilter) request.getDeviceFilter()); 1476ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mScanFilter = mFilter.getScanFilter(); 1486ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1496ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla reset(); 1506ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final IntentFilter intentFilter = new IntentFilter(); 1526ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla intentFilter.addAction(BluetoothDevice.ACTION_FOUND); 1536ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED); 1546ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1556ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla registerReceiver(mBluetoothDeviceFoundBroadcastReceiver, intentFilter); 1566ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBluetoothAdapter.startDiscovery(); 1576ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBLEScanner.startScan( 1596ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Collections.singletonList(mScanFilter), mDefaultScanSettings, mBLEScanCallback); 1606ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1616ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1626ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void reset() { 1636ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.clear(); 1646ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mSelectedDevice = null; 1656ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 1666ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1676ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1686ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1696ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public boolean onUnbind(Intent intent) { 1706ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla stopScan(); 1716ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return super.onUnbind(intent); 1726ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1736ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1746ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void stopScan() { 1756ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) Log.i(LOG_TAG, "stopScan() called"); 1766ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBluetoothAdapter.cancelDiscovery(); 1776ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBLEScanner.stopScan(mBLEScanCallback); 1786ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla unregisterReceiver(mBluetoothDeviceFoundBroadcastReceiver); 1796ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla stopSelf(); 1806ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1816ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1826ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void onDeviceFound(BluetoothDevice device) { 1836ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (mDevicesFound.contains(device)) { 1846ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return; 1856ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1866ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1876ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) { 1886ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Log.i(LOG_TAG, "Considering device " + getDeviceDisplayName(device)); 1896ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1906ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1916ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (!mFilter.matches(device)) { 1926ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return; 1936ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1946ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1956ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) { 1966ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Log.i(LOG_TAG, "Found device " + getDeviceDisplayName(device)); 1976ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1986ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (mDevicesFound.isEmpty()) { 1996ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onReadyToShowUI(); 2006ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2016ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.add(device); 2026ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 2036ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2046ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2056ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla //TODO also, on timeout -> call onFailure 2066ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void onReadyToShowUI() { 2076ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla try { 2086ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mCallback.onSuccess(PendingIntent.getActivity( 2096ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla this, 0, 2106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla new Intent(this, DeviceChooserActivity.class), 2116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT 2126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla | PendingIntent.FLAG_IMMUTABLE)); 2136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } catch (RemoteException e) { 2146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla throw new RuntimeException(e); 2156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2166ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void onDeviceLost(BluetoothDevice device) { 2196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.remove(device); 2206ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 2216ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) { 2226ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Log.i(LOG_TAG, "Lost device " + getDeviceDisplayName(device)); 2236ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2246ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2256ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla class DevicesAdapter extends ArrayAdapter<BluetoothDevice> { 2276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); 2286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private Drawable icon(int drawableRes) { 2306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Drawable icon = getResources().getDrawable(drawableRes, null); 2316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla icon.setTint(Color.DKGRAY); 2326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return icon; 2336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2346ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public DevicesAdapter() { 2366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla super(DeviceDiscoveryService.this, 0, mDevicesFound); 2376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 2406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public View getView( 2416ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla int position, 2426ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Nullable View convertView, 2436ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @NonNull ViewGroup parent) { 2446ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla TextView view = convertView instanceof TextView 2456ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla ? (TextView) convertView 2466ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla : newView(); 2476ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla bind(view, getItem(position)); 2486ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return view; 2496ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2506ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void bind(TextView textView, BluetoothDevice device) { 2526ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setText(getDeviceDisplayName(device)); 2536ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setBackgroundColor( 2546ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla device.equals(mSelectedDevice) 2556ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla ? Color.GRAY 2566ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla : Color.TRANSPARENT); 2576ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setOnClickListener((view) -> { 2586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mSelectedDevice = device; 2596ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla notifyDataSetChanged(); 2606ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }); 2616ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2626ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2636ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla //TODO move to a layout file 2646ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private TextView newView() { 2656ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final TextView textView = new TextView(DeviceDiscoveryService.this); 2666ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setTextColor(Color.BLACK); 2676ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final int padding = DeviceChooserActivity.getPadding(getResources()); 2686ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setPadding(padding, padding, padding, padding); 2696ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setCompoundDrawablesWithIntrinsicBounds( 2706ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla BLUETOOTH_ICON, null, null, null); 2716ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setCompoundDrawablePadding(padding); 2726ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return textView; 2736ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2746ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2756ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla} 276