Update samples prebuilts for lmp-mr1-ub-docs

Synced to developers/samples/android commit
54bab34b386e343e9d0ea75a5fb8d13db2c71eb5.

Change-Id: I1c9d9d2c1f53a051d7b4d85303d5c01ab6f16e68
This commit is contained in:
Trevor Johns
2015-05-08 19:29:42 -07:00
parent 03236391f1
commit 434d41c45d
55 changed files with 1823 additions and 201 deletions

View File

@@ -0,0 +1,190 @@
package com.example.android.bluetoothadvertisements;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
import android.widget.Toast;
/**
* Allows user to start & stop Bluetooth LE Advertising of their device.
*/
public class AdvertiserFragment extends Fragment {
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private AdvertiseCallback mAdvertiseCallback;
private Switch mSwitch;
/**
* Must be called after object creation by MainActivity.
*
* @param btAdapter the local BluetoothAdapter
*/
public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
this.mBluetoothAdapter = btAdapter;
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_advertiser, container, false);
mSwitch = (Switch) view.findViewById(R.id.advertise_switch);
mSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onSwitchClicked(v);
}
});
return view;
}
@Override
public void onStop() {
super.onStop();
if(mAdvertiseCallback != null){
stopAdvertising();
}
}
/**
* Called when switch is toggled - starts or stops advertising.
*
* @param view is the Switch View object
*/
public void onSwitchClicked(View view) {
// Is the toggle on?
boolean on = ((Switch) view).isChecked();
if (on) {
startAdvertising();
} else {
stopAdvertising();
}
}
/**
* Starts BLE Advertising.
*/
private void startAdvertising() {
mAdvertiseCallback = new SampleAdvertiseCallback();
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(),
mAdvertiseCallback);
} else {
mSwitch.setChecked(false);
Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show();
}
}
/**
* Stops BLE Advertising.
*/
private void stopAdvertising() {
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
} else {
mSwitch.setChecked(false);
Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show();
}
}
/**
* Returns an AdvertiseData object which includes the Service UUID and Device Name.
*/
private AdvertiseData buildAdvertiseData() {
// Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements.
// This includes everything put into AdvertiseData including UUIDs, device info, &
// arbitrary service or manufacturer data.
// Attempting to send packets over this limit will result in a failure with error code
// AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the
// onStartFailure() method of an AdvertiseCallback implementation.
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(Constants.Service_UUID);
dataBuilder.setIncludeDeviceName(true);
return dataBuilder.build();
}
/**
* Returns an AdvertiseSettings object set to use low power (to help preserve battery life).
*/
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
return settingsBuilder.build();
}
/**
* Custom callback after Advertising succeeds or fails to start.
*/
private class SampleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
mSwitch.setChecked(false);
String errorMessage = getString(R.string.start_error_prefix);
switch (errorCode) {
case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
errorMessage += " " + getString(R.string.start_error_already_started);
break;
case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
errorMessage += " " + getString(R.string.start_error_too_large);
break;
case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
errorMessage += " " + getString(R.string.start_error_unsupported);
break;
case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
errorMessage += " " + getString(R.string.start_error_internal);
break;
case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
errorMessage += " " + getString(R.string.start_error_too_many);
break;
}
Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
// Don't need to do anything here, advertising successfully started.
}
}
}

View File

@@ -0,0 +1,22 @@
package com.example.android.bluetoothadvertisements;
import android.os.ParcelUuid;
/**
* Constants for use in the Bluetooth Advertisements sample
*/
public class Constants {
/**
* UUID identified with this app - set as Service UUID for BLE Advertisements.
*
* Bluetooth requires a certain format for UUIDs associated with Services.
* The official specification can be found here:
* {@link https://www.bluetooth.org/en-us/specification/assigned-numbers/service-discovery}
*/
public static final ParcelUuid Service_UUID = ParcelUuid
.fromString("0000b81d-0000-1000-8000-00805f9b34fb");
public static final int REQUEST_ENABLE_BT = 1;
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2013 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.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.widget.TextView;
import android.widget.Toast;
/**
* Setup display fragments and ensure the device supports Bluetooth.
*/
public class MainActivity extends FragmentActivity {
private BluetoothAdapter mBluetoothAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(R.string.activity_main_title);
if (savedInstanceState == null ) {
mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE))
.getAdapter();
// Is Bluetooth supported on this device?
if (mBluetoothAdapter != null) {
// Is Bluetooth turned on?
if (mBluetoothAdapter.isEnabled()) {
// Are Bluetooth Advertisements supported on this device?
if (mBluetoothAdapter.isMultipleAdvertisementSupported()) {
// Everything is supported and enabled, load the fragments.
setupFragments();
} else {
// Bluetooth Advertisements are not supported.
showErrorText(R.string.bt_ads_not_supported);
}
} else {
// Prompt user to turn on Bluetooth (logic continues in onActivityResult()).
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT);
}
} else {
// Bluetooth is not supported.
showErrorText(R.string.bt_not_supported);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case Constants.REQUEST_ENABLE_BT:
if (resultCode == RESULT_OK) {
// Bluetooth is now Enabled, are Bluetooth Advertisements supported on
// this device?
if (mBluetoothAdapter.isMultipleAdvertisementSupported()) {
// Everything is supported and enabled, load the fragments.
setupFragments();
} else {
// Bluetooth Advertisements are not supported.
showErrorText(R.string.bt_ads_not_supported);
}
} else {
// User declined to enable Bluetooth, exit the app.
Toast.makeText(this, R.string.bt_not_enabled_leaving,
Toast.LENGTH_SHORT).show();
finish();
}
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
private void setupFragments() {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
ScannerFragment scannerFragment = new ScannerFragment();
scannerFragment.setBluetoothAdapter(mBluetoothAdapter);
transaction.replace(R.id.scanner_fragment_container, scannerFragment);
AdvertiserFragment advertiserFragment = new AdvertiserFragment();
advertiserFragment.setBluetoothAdapter(mBluetoothAdapter);
transaction.replace(R.id.advertiser_fragment_container, advertiserFragment);
transaction.commit();
}
private void showErrorText(int messageId) {
TextView view = (TextView) findViewById(R.id.error_textview);
view.setText(getString(messageId));
}
}

View File

@@ -0,0 +1,147 @@
package com.example.android.bluetoothadvertisements;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/**
* Holds and displays {@link ScanResult}s, used by {@link ScannerFragment}.
*/
public class ScanResultAdapter extends BaseAdapter {
private ArrayList<ScanResult> mArrayList;
private Context mContext;
private LayoutInflater mInflater;
ScanResultAdapter(Context context, LayoutInflater inflater) {
super();
mContext = context;
mInflater = inflater;
mArrayList = new ArrayList<>();
}
@Override
public int getCount() {
return mArrayList.size();
}
@Override
public Object getItem(int position) {
return mArrayList.get(position);
}
@Override
public long getItemId(int position) {
return mArrayList.get(position).getDevice().getAddress().hashCode();
}
@Override
public View getView(int position, View view, ViewGroup parent) {
// Reuse an old view if we can, otherwise create a new one.
if (view == null) {
view = mInflater.inflate(R.layout.listitem_scanresult, null);
}
TextView deviceNameView = (TextView) view.findViewById(R.id.device_name);
TextView deviceAddressView = (TextView) view.findViewById(R.id.device_address);
TextView lastSeenView = (TextView) view.findViewById(R.id.last_seen);
ScanResult scanResult = mArrayList.get(position);
deviceNameView.setText(scanResult.getDevice().getName());
deviceAddressView.setText(scanResult.getDevice().getAddress());
lastSeenView.setText(getTimeSinceString(mContext, scanResult.getTimestampNanos()));
return view;
}
/**
* Search the adapter for an existing device address and return it, otherwise return -1.
*/
private int getPosition(String address) {
int position = -1;
for (int i = 0; i < mArrayList.size(); i++) {
if (mArrayList.get(i).getDevice().getAddress().equals(address)) {
position = i;
break;
}
}
return position;
}
/**
* Add a ScanResult item to the adapter if a result from that device isn't already present.
* Otherwise updates the existing position with the new ScanResult.
*/
public void add(ScanResult scanResult) {
int existingPosition = getPosition(scanResult.getDevice().getAddress());
if (existingPosition >= 0) {
// Device is already in list, update its record.
mArrayList.set(existingPosition, scanResult);
} else {
// Add new Device's ScanResult to list.
mArrayList.add(scanResult);
}
}
/**
* Clear out the adapter.
*/
public void clear() {
mArrayList.clear();
}
/**
* Takes in a number of nanoseconds and returns a human-readable string giving a vague
* description of how long ago that was.
*/
public static String getTimeSinceString(Context context, long timeNanoseconds) {
String lastSeenText = context.getResources().getString(R.string.last_seen) + " ";
long timeSince = SystemClock.elapsedRealtimeNanos() - timeNanoseconds;
long secondsSince = TimeUnit.SECONDS.convert(timeSince, TimeUnit.NANOSECONDS);
if (secondsSince < 5) {
lastSeenText += context.getResources().getString(R.string.just_now);
} else if (secondsSince < 60) {
lastSeenText += secondsSince + " " + context.getResources()
.getString(R.string.seconds_ago);
} else {
long minutesSince = TimeUnit.MINUTES.convert(secondsSince, TimeUnit.SECONDS);
if (minutesSince < 60) {
if (minutesSince == 1) {
lastSeenText += minutesSince + " " + context.getResources()
.getString(R.string.minute_ago);
} else {
lastSeenText += minutesSince + " " + context.getResources()
.getString(R.string.minutes_ago);
}
} else {
long hoursSince = TimeUnit.HOURS.convert(minutesSince, TimeUnit.MINUTES);
if (hoursSince == 1) {
lastSeenText += hoursSince + " " + context.getResources()
.getString(R.string.hour_ago);
} else {
lastSeenText += hoursSince + " " + context.getResources()
.getString(R.string.hours_ago);
}
}
}
return lastSeenText;
}
}

View File

@@ -0,0 +1,212 @@
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();
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();
}
}
}