Syncing to //developers/samples/android commmit 2be5f5ca32. Change-Id: Ia08c63655bd122c7fbb0cc3a50eec95d8edcb3f1
230 lines
7.2 KiB
Java
230 lines
7.2 KiB
Java
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.example.android.bluetoothadvertisements;
|
|
|
|
import android.bluetooth.BluetoothAdapter;
|
|
import android.bluetooth.le.BluetoothLeScanner;
|
|
import android.bluetooth.le.ScanCallback;
|
|
import android.bluetooth.le.ScanFilter;
|
|
import android.bluetooth.le.ScanResult;
|
|
import android.bluetooth.le.ScanSettings;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.support.v4.app.ListFragment;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.Toast;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
* Scans for Bluetooth Low Energy Advertisements matching a filter and displays them to the user.
|
|
*/
|
|
public class ScannerFragment extends ListFragment {
|
|
|
|
private static final String TAG = ScannerFragment.class.getSimpleName();
|
|
|
|
/**
|
|
* Stops scanning after 5 seconds.
|
|
*/
|
|
private static final long SCAN_PERIOD = 5000;
|
|
|
|
private BluetoothAdapter mBluetoothAdapter;
|
|
|
|
private BluetoothLeScanner mBluetoothLeScanner;
|
|
|
|
private ScanCallback mScanCallback;
|
|
|
|
private ScanResultAdapter mAdapter;
|
|
|
|
private Handler mHandler;
|
|
|
|
/**
|
|
* Must be called after object creation by MainActivity.
|
|
*
|
|
* @param btAdapter the local BluetoothAdapter
|
|
*/
|
|
public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
|
|
this.mBluetoothAdapter = btAdapter;
|
|
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setHasOptionsMenu(true);
|
|
setRetainInstance(true);
|
|
|
|
// Use getActivity().getApplicationContext() instead of just getActivity() because this
|
|
// object lives in a fragment and needs to be kept separate from the Activity lifecycle.
|
|
//
|
|
// We could get a LayoutInflater from the ApplicationContext but it messes with the
|
|
// default theme, so generate it from getActivity() and pass it in separately.
|
|
mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(),
|
|
LayoutInflater.from(getActivity()));
|
|
mHandler = new Handler();
|
|
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
Bundle savedInstanceState) {
|
|
|
|
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
|
|
|
setListAdapter(mAdapter);
|
|
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
super.onViewCreated(view, savedInstanceState);
|
|
|
|
getListView().setDivider(null);
|
|
getListView().setDividerHeight(0);
|
|
|
|
setEmptyText(getString(R.string.empty_list));
|
|
|
|
// Trigger refresh on app's 1st load
|
|
startScanning();
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
inflater.inflate(R.menu.scanner_menu, menu);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
|
case R.id.refresh:
|
|
startScanning();
|
|
return true;
|
|
default:
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start scanning for BLE Advertisements (& set it up to stop after a set period of time).
|
|
*/
|
|
public void startScanning() {
|
|
if (mScanCallback == null) {
|
|
Log.d(TAG, "Starting Scanning");
|
|
|
|
// Will stop the scanning after a set time.
|
|
mHandler.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
stopScanning();
|
|
}
|
|
}, SCAN_PERIOD);
|
|
|
|
// Kick off a new scan.
|
|
mScanCallback = new SampleScanCallback();
|
|
mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);
|
|
|
|
String toastText = getString(R.string.scan_start_toast) + " "
|
|
+ TimeUnit.SECONDS.convert(SCAN_PERIOD, TimeUnit.MILLISECONDS) + " "
|
|
+ getString(R.string.seconds);
|
|
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
|
|
} else {
|
|
Toast.makeText(getActivity(), R.string.already_scanning, Toast.LENGTH_SHORT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop scanning for BLE Advertisements.
|
|
*/
|
|
public void stopScanning() {
|
|
Log.d(TAG, "Stopping Scanning");
|
|
|
|
// Stop the scan, wipe the callback.
|
|
mBluetoothLeScanner.stopScan(mScanCallback);
|
|
mScanCallback = null;
|
|
|
|
// Even if no new results, update 'last seen' times.
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
/**
|
|
* Return a List of {@link ScanFilter} objects to filter by Service UUID.
|
|
*/
|
|
private List<ScanFilter> buildScanFilters() {
|
|
List<ScanFilter> scanFilters = new ArrayList<>();
|
|
|
|
ScanFilter.Builder builder = new ScanFilter.Builder();
|
|
// Comment out the below line to see all BLE devices around you
|
|
builder.setServiceUuid(Constants.Service_UUID);
|
|
scanFilters.add(builder.build());
|
|
|
|
return scanFilters;
|
|
}
|
|
|
|
/**
|
|
* Return a {@link ScanSettings} object set to use low power (to preserve battery life).
|
|
*/
|
|
private ScanSettings buildScanSettings() {
|
|
ScanSettings.Builder builder = new ScanSettings.Builder();
|
|
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
|
|
return builder.build();
|
|
}
|
|
|
|
/**
|
|
* Custom ScanCallback object - adds to adapter on success, displays error on failure.
|
|
*/
|
|
private class SampleScanCallback extends ScanCallback {
|
|
|
|
@Override
|
|
public void onBatchScanResults(List<ScanResult> results) {
|
|
super.onBatchScanResults(results);
|
|
|
|
for (ScanResult result : results) {
|
|
mAdapter.add(result);
|
|
}
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
@Override
|
|
public void onScanResult(int callbackType, ScanResult result) {
|
|
super.onScanResult(callbackType, result);
|
|
|
|
mAdapter.add(result);
|
|
mAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
@Override
|
|
public void onScanFailed(int errorCode) {
|
|
super.onScanFailed(errorCode);
|
|
Toast.makeText(getActivity(), "Scan failed with error: " + errorCode, Toast.LENGTH_LONG)
|
|
.show();
|
|
}
|
|
}
|
|
}
|