diff options
author | Clare Bayley <clareb@google.com> | 2015-03-18 14:36:13 -0700 |
---|---|---|
committer | Clare Bayley <clareb@google.com> | 2015-04-07 14:25:49 -0700 |
commit | ad9ed1c914d3dc3d59c497e8f123aa1dfdf11767 (patch) | |
tree | 875188dc7680edecb5cb748db264dc51ea17ca4d /connectivity | |
parent | 650a11089dbaf5e7500a598f7e82e5a7b070e31f (diff) | |
download | android-ad9ed1c914d3dc3d59c497e8f123aa1dfdf11767.tar.gz |
New BLE Advertisements Sample
Change-Id: I144987adf2739d0fbc68ad87380518680eedfa6e
Diffstat (limited to 'connectivity')
31 files changed, 1346 insertions, 0 deletions
diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/.gitignore b/connectivity/bluetooth/BluetoothAdvertisements/Application/.gitignore new file mode 100644 index 00000000..6eb878d4 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/.gitignore @@ -0,0 +1,16 @@ +# 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. +src/template/ +src/common/ +build.gradle diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/proguard-project.txt b/connectivity/bluetooth/BluetoothAdvertisements/Application/proguard-project.txt new file mode 100644 index 00000000..0d8f171d --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/proguard-project.txt @@ -0,0 +1,20 @@ + To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/AndroidManifest.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/AndroidManifest.xml new file mode 100644 index 00000000..48084fc9 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.bluetoothadvertisements" + android:versionCode="1" + android:versionName="1.0"> + + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> + <uses-permission android:name="android.permission.BLUETOOTH"/> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/AdvertiserFragment.java b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/AdvertiserFragment.java new file mode 100644 index 00000000..f8daefb0 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/AdvertiserFragment.java @@ -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. + } + } + +} diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java new file mode 100644 index 00000000..d3941e2a --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/Constants.java @@ -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; + +} diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java new file mode 100644 index 00000000..f0044a3e --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/MainActivity.java @@ -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)); + } +}
\ No newline at end of file diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScanResultAdapter.java b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScanResultAdapter.java new file mode 100644 index 00000000..0f905ea7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScanResultAdapter.java @@ -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; + } +} diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java new file mode 100644 index 00000000..b9ad4d96 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/java/com/example/android/bluetoothadvertisements/ScannerFragment.java @@ -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(); + } + } +} diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_action_refresh.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 00000000..dae27903 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_action_refresh.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..b1efaf4b --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_action_refresh.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 00000000..94ab6f4c --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_action_refresh.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..f5f9244f --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_action_refresh.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 00000000..ab4ab9da --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_action_refresh.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..5d07b3f0 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_action_refresh.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 00000000..44ee117e --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_action_refresh.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..6ef21e1f --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/activity_main.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/activity_main.xml new file mode 100755 index 00000000..817cccc4 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/activity_main.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + + <FrameLayout + android:id="@+id/scanner_fragment_container" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="0px" > + + <TextView + android:id="@+id/error_textview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:freezesText="true" /> + + </FrameLayout> + + <FrameLayout + android:id="@+id/advertiser_fragment_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <!--<fragment--> + <!--android:name="com.example.android.bluetoothadvertisements.ScannerFragment"--> + <!--android:id="@+id/scanner_fragment"--> + <!--android:layout_width="match_parent"--> + <!--android:layout_height="wrap_content" />--> + + <!--<fragment--> + <!--android:name="com.example.android.bluetoothadvertisements.AdvertiserFragment"--> + <!--android:id="@+id/advertiser_fragment"--> + <!--android:layout_width="match_parent"--> + <!--android:layout_height="wrap_content" />--> + +</LinearLayout> diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/fragment_advertiser.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/fragment_advertiser.xml new file mode 100644 index 00000000..4031b8d7 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/fragment_advertiser.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:context="com.example.android.bluetoothadvertisements.AdvertiserFragment"> + + <!-- Horizontal Divider --> + <View + android:layout_width="250dp" + android:layout_height="1dp" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="true" + android:background="@android:color/darker_gray"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_centerInParent="true" + android:paddingTop="20dp" + android:paddingBottom="20dp" > + + <TextView + android:text="@string/broadcast_device" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="100dp" + android:padding="5dp"/> + + <Switch + android:id="@+id/advertise_switch" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:switchMinWidth="80dp" /> + + </LinearLayout> + +</RelativeLayout> diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/listitem_scanresult.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/listitem_scanresult.xml new file mode 100644 index 00000000..ff5956fb --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/layout/listitem_scanresult.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + +<LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="20dp" + android:paddingLeft="100dp" + android:paddingRight="100dp"> + <TextView android:id="@+id/device_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16dp"/> + + <TextView android:id="@+id/device_address" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp"/> + <TextView android:id="@+id/last_seen" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp"/> +</LinearLayout> +</RelativeLayout>
\ No newline at end of file diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/menu-v21/scanner_menu.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/menu-v21/scanner_menu.xml new file mode 100644 index 00000000..8dda284c --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/menu-v21/scanner_menu.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/refresh" + android:title="@string/refresh" + android:showAsAction="always" + android:icon="@drawable/ic_action_refresh" + /> +</menu>
\ No newline at end of file diff --git a/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/values/strings.xml b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/values/strings.xml new file mode 100644 index 00000000..0ea9c833 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/Application/src/main/res/values/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="bt_not_enabled_leaving">User declined to enable Bluetooth, exiting Bluetooth Advertisements.</string> + <string name="activity_main_title">Nearby Devices</string> + <string name="broadcast_device">Broadcast Device</string> + + <string name="bt_not_supported">Bluetooth is not supported on this device.</string> + <string name="bt_ads_not_supported">Bluetooth Advertisements are not supported on this device.</string> + + <string name="refresh">Refresh</string> + <string name="start_error_prefix">Start Advertising failed: </string> + <string name="start_error_already_started">already started.</string> + <string name="start_error_too_large">data packet exceeded 31 Byte limit.</string> + <string name="start_error_unsupported">not supported on this device.</string> + <string name="start_error_internal">internal error.</string> + <string name="start_error_too_many">too many advertisers.</string> + <string name="bt_null">Error: Bluetooth object null</string> + <string name="last_seen">Last Seen:</string> + <string name="just_now">just now</string> + <string name="minute_ago">minute ago</string> + <string name="hour_ago">hour ago</string> + <string name="seconds_ago">seconds ago</string> + <string name="minutes_ago">minutes ago</string> + <string name="hours_ago">hours ago</string> + <string name="empty_list">No devices found - refresh to try again.</string> + <string name="seconds">seconds.</string> + <string name="scan_start_toast">Scanning for</string> + +</resources>
\ No newline at end of file diff --git a/connectivity/bluetooth/BluetoothAdvertisements/build.gradle b/connectivity/bluetooth/BluetoothAdvertisements/build.gradle new file mode 100644 index 00000000..18f393f9 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/build.gradle @@ -0,0 +1,11 @@ + +// BEGIN_EXCLUDE +import com.example.android.samples.build.SampleGenPlugin +apply plugin: SampleGenPlugin + +samplegen { + pathToBuild "../../../../../build" + pathToSamplesCommon "../../../common" +} +apply from: "../../../../../build/build.gradle" +// END_EXCLUDE diff --git a/connectivity/bluetooth/BluetoothAdvertisements/buildSrc/build.gradle b/connectivity/bluetooth/BluetoothAdvertisements/buildSrc/build.gradle new file mode 100644 index 00000000..7cebf71c --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/buildSrc/build.gradle @@ -0,0 +1,15 @@ +repositories { + mavenCentral() +} +dependencies { + compile 'org.freemarker:freemarker:2.3.20' +} + +sourceSets { + main { + groovy { + srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy") + } + } +} + diff --git a/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.jar b/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..8c0fb64a --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.jar diff --git a/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..8712eac8 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Mar 02 15:22:07 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/connectivity/bluetooth/BluetoothAdvertisements/gradlew b/connectivity/bluetooth/BluetoothAdvertisements/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/connectivity/bluetooth/BluetoothAdvertisements/gradlew.bat b/connectivity/bluetooth/BluetoothAdvertisements/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/connectivity/bluetooth/BluetoothAdvertisements/screenshots/1-main.png b/connectivity/bluetooth/BluetoothAdvertisements/screenshots/1-main.png Binary files differnew file mode 100644 index 00000000..0c537fa2 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/screenshots/1-main.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/screenshots/icon-web.png b/connectivity/bluetooth/BluetoothAdvertisements/screenshots/icon-web.png Binary files differnew file mode 100644 index 00000000..0a6d413a --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/screenshots/icon-web.png diff --git a/connectivity/bluetooth/BluetoothAdvertisements/settings.gradle b/connectivity/bluetooth/BluetoothAdvertisements/settings.gradle new file mode 100644 index 00000000..9464a359 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/settings.gradle @@ -0,0 +1 @@ +include 'Application' diff --git a/connectivity/bluetooth/BluetoothAdvertisements/template-params.xml b/connectivity/bluetooth/BluetoothAdvertisements/template-params.xml new file mode 100644 index 00000000..4bd4f2a0 --- /dev/null +++ b/connectivity/bluetooth/BluetoothAdvertisements/template-params.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<sample> + <name>BluetoothAdvertisements</name> + <group>Connectivity</group> + <package>com.example.android.bluetoothadvertisements</package> + + <minSdk>21</minSdk> + + + <strings> + <intro> +<![CDATA[ +This samples demonstrates how to use the Bluetooth Low Power Advertisements API +with a single device acting as both scanner and advertiser. +]]> + </intro> + </strings> + + <colors> + <color> + <name>colorPrimary</name> + <hexval>#434AB3</hexval> + </color> + <color> + <name>colorPrimaryDark</name> + <hexval>#34379D</hexval> + </color> + <color> + <name>textColorPrimary</name> + <hexval>#FFFFFF</hexval> + </color> + </colors> + + <template src="base" /> + + + <metadata> + <status>DRAFT</status> + <categories>Connectivity</categories> + <technologies>Android</technologies> + <languages>Java</languages> + <solutions>Mobile</solutions> + <level>ADVANCED</level> + <icon>screenshots/icon-web.png</icon> + <screenshots> + <img>screenshots/1-main.png</img> + </screenshots> + <api_refs> + <android>android.bluetooth.le.BluetoothLeAdvertiser</android> + <android>android.bluetooth.le.BluetoothLeScanner</android> + </api_refs> + + <description> +<![CDATA[ +Sample demonstrating how to advertise small amounts of data using the Bluetooth +Low Energy API. Also demonstrates how to scan for those Advertisements. (requires +2 devices to see full operation) +]]> + </description> + + <intro> +<![CDATA[ +This sample demonstrates use of the Bluetooth Low Energy (BLE) [Advertisement][1] and [Scanning][2] APIs. +The functionality is split into two fragments - one for Advertising, one for Scanning. + +ScannerFragment activates BLE Scanning for 5 seconds and displays a list of found devices which are advertising +using this sample. It shows the device type, Bluetooth address, and when it was last seen. User can +refresh to scan again and update the list. + +AdvertiserFragment allows the user to toggle BLE Advertising of that device. It broadcasts basic +information about the device along with a UUID specific to this app so the ScannerFragment on other +devices can filter by it. + +Note: A device cannot detect its own BLE advertisements. You will need two devices to see this +sample in action. + +[1]:https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html +[2]:https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html +]]> + </intro> + </metadata> +</sample> |