Sync mnc-dev sample prebuilts

Syncing to //developers/samples/android commmit 2be5f5ca32.

Change-Id: Ia08c63655bd122c7fbb0cc3a50eec95d8edcb3f1
This commit is contained in:
Trevor Johns
2015-08-17 09:37:47 -07:00
parent a56a634166
commit c8e94bf583
28 changed files with 734 additions and 323 deletions

View File

@@ -16,11 +16,11 @@
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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -32,67 +32,121 @@ 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;
public class AdvertiserFragment extends Fragment implements View.OnClickListener {
/**
* Lets user toggle BLE Advertising.
*/
private Switch mSwitch;
/**
* Must be called after object creation by MainActivity.
*
* @param btAdapter the local BluetoothAdapter
* Listens for notifications that the {@code AdvertiserService} has failed to start advertising.
* This Receiver deals with Fragment UI elements and only needs to be active when the Fragment
* is on-screen, so it's defined and registered in code instead of the Manifest.
*/
public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
this.mBluetoothAdapter = btAdapter;
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
private BroadcastReceiver advertisingFailureReceiver;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
advertisingFailureReceiver = new BroadcastReceiver() {
/**
* Receives Advertising error codes from {@code AdvertiserService} and displays error messages
* to the user. Sets the advertising toggle to 'false.'
*/
@Override
public void onReceive(Context context, Intent intent) {
int errorCode = intent.getIntExtra(AdvertiserService.ADVERTISING_FAILED_EXTRA_CODE, -1);
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;
case AdvertiserService.ADVERTISING_TIMED_OUT:
errorMessage = " " + getString(R.string.advertising_timedout);
break;
default:
errorMessage += " " + getString(R.string.start_error_unknown);
}
Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
}
};
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
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);
}
});
mSwitch.setOnClickListener(this);
return view;
}
/**
* When app comes on screen, check if BLE Advertisements are running, set switch accordingly,
* and register the Receiver to be notified if Advertising fails.
*/
@Override
public void onStop() {
super.onStop();
public void onResume() {
super.onResume();
if(mAdvertiseCallback != null){
stopAdvertising();
if (AdvertiserService.running) {
mSwitch.setChecked(true);
} else {
mSwitch.setChecked(false);
}
IntentFilter failureFilter = new IntentFilter(AdvertiserService.ADVERTISING_FAILED);
getActivity().registerReceiver(advertisingFailureReceiver, failureFilter);
}
/**
* When app goes off screen, unregister the Advertising failure Receiver to stop memory leaks.
* (and because the app doesn't care if Advertising fails while the UI isn't active)
*/
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(advertisingFailureReceiver);
}
/**
* Returns Intent addressed to the {@code AdvertiserService} class.
*/
private static Intent getServiceIntent(Context c) {
return new Intent(c, AdvertiserService.class);
}
/**
* Called when switch is toggled - starts or stops advertising.
*
* @param view is the Switch View object
*/
public void onSwitchClicked(View view) {
@Override
public void onClick(View v) {
// Is the toggle on?
boolean on = ((Switch) view).isChecked();
boolean on = ((Switch) v).isChecked();
if (on) {
startAdvertising();
@@ -102,105 +156,20 @@ public class AdvertiserFragment extends Fragment {
}
/**
* Starts BLE Advertising.
* Starts BLE Advertising by starting {@code AdvertiserService}.
*/
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();
}
Context c = getActivity();
c.startService(getServiceIntent(c));
}
/**
* Stops BLE Advertising.
* Stops BLE Advertising by stopping {@code AdvertiserService}.
*/
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();
}
Context c = getActivity();
c.stopService(getServiceIntent(c));
mSwitch.setChecked(false);
}
/**
* 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,223 @@
package com.example.android.bluetoothadvertisements;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import java.util.concurrent.TimeUnit;
/**
* Manages BLE Advertising independent of the main app.
* If the app goes off screen (or gets killed completely) advertising can continue because this
* Service is maintaining the necessary Callback in memory.
*/
public class AdvertiserService extends Service {
private static final String TAG = AdvertiserService.class.getSimpleName();
/**
* A global variable to let AdvertiserFragment check if the Service is running without needing
* to start or bind to it.
* This is the best practice method as defined here:
* https://groups.google.com/forum/#!topic/android-developers/jEvXMWgbgzE
*/
public static boolean running = false;
public static final String ADVERTISING_FAILED =
"com.example.android.bluetoothadvertisements.advertising_failed";
public static final String ADVERTISING_FAILED_EXTRA_CODE = "failureCode";
public static final int ADVERTISING_TIMED_OUT = 6;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private AdvertiseCallback mAdvertiseCallback;
private Handler mHandler;
private Runnable timeoutRunnable;
/**
* Length of time to allow advertising before automatically shutting off. (10 minutes)
*/
private long TIMEOUT = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES);
@Override
public void onCreate() {
running = true;
initialize();
startAdvertising();
setTimeout();
super.onCreate();
}
@Override
public void onDestroy() {
/**
* Note that onDestroy is not guaranteed to be called quickly or at all. Services exist at
* the whim of the system, and onDestroy can be delayed or skipped entirely if memory need
* is critical.
*/
running = false;
stopAdvertising();
mHandler.removeCallbacks(timeoutRunnable);
super.onDestroy();
}
/**
* Required for extending service, but this will be a Started Service only, so no need for
* binding.
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Get references to system Bluetooth objects if we don't have them already.
*/
private void initialize() {
if (mBluetoothLeAdvertiser == null) {
BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter != null) {
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
} else {
Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
}
}
}
/**
* Starts a delayed Runnable that will cause the BLE Advertising to timeout and stop after a
* set amount of time.
*/
private void setTimeout(){
mHandler = new Handler();
timeoutRunnable = new Runnable() {
@Override
public void run() {
Log.d(TAG, "AdvertiserService has reached timeout of "+TIMEOUT+" milliseconds, stopping advertising.");
sendFailureIntent(ADVERTISING_TIMED_OUT);
stopSelf();
}
};
mHandler.postDelayed(timeoutRunnable, TIMEOUT);
}
/**
* Starts BLE Advertising.
*/
private void startAdvertising() {
Log.d(TAG, "Service: Starting Advertising");
if (mAdvertiseCallback == null) {
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
mAdvertiseCallback = new SampleAdvertiseCallback();
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.startAdvertising(settings, data,
mAdvertiseCallback);
}
}
}
/**
* Stops BLE Advertising.
*/
private void stopAdvertising() {
Log.d(TAG, "Service: Stopping Advertising");
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
/**
* 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);
/* For example - this will cause advertising to fail (exceeds size limit) */
//String failureData = "asdghkajsghalkxcjhfa;sghtalksjcfhalskfjhasldkjfhdskf";
//dataBuilder.addServiceData(Constants.Service_UUID, failureData.getBytes());
return dataBuilder.build();
}
/**
* Returns an AdvertiseSettings object set to use low power (to help preserve battery life)
* and disable the built-in timeout since this code uses its own timeout runnable.
*/
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
/**
* Custom callback after Advertising succeeds or fails to start. Broadcasts the error code
* in an Intent to be picked up by AdvertiserFragment and stops this Service.
*/
private class SampleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "Advertising failed");
sendFailureIntent(errorCode);
stopSelf();
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "Advertising successfully started");
}
}
/**
* Builds and sends a broadcast intent indicating Advertising has failed. Includes the error
* code as an extra. This is intended to be picked up by the {@code AdvertiserFragment}.
*/
private void sendFailureIntent(int errorCode){
Intent failureIntent = new Intent();
failureIntent.setAction(ADVERTISING_FAILED);
failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE, errorCode);
sendBroadcast(failureIntent);
}
}

View File

@@ -39,7 +39,7 @@ public class MainActivity extends FragmentActivity {
setContentView(R.layout.activity_main);
setTitle(R.string.activity_main_title);
if (savedInstanceState == null ) {
if (savedInstanceState == null) {
mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE))
.getAdapter();
@@ -112,11 +112,11 @@ public class MainActivity extends FragmentActivity {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
ScannerFragment scannerFragment = new ScannerFragment();
// Fragments can't access system services directly, so pass it the BluetoothAdapter
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();

View File

@@ -75,7 +75,11 @@ public class ScanResultAdapter extends BaseAdapter {
ScanResult scanResult = mArrayList.get(position);
deviceNameView.setText(scanResult.getDevice().getName());
String name = scanResult.getDevice().getName();
if (name == null) {
name = mContext.getResources().getString(R.string.no_name);
}
deviceNameView.setText(name);
deviceAddressView.setText(scanResult.getDevice().getAddress());
lastSeenView.setText(getTimeSinceString(mContext, scanResult.getTimestampNanos()));

View File

@@ -83,7 +83,7 @@ public class ScannerFragment extends ListFragment {
// 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()));
LayoutInflater.from(getActivity()));
mHandler = new Handler();
}
@@ -180,6 +180,7 @@ public class ScannerFragment extends ListFragment {
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());