LAMP_Connect_v1
2
wear/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
57
wear/build.gradle
Normal file
@@ -0,0 +1,57 @@
|
||||
// module :wear build.gradle for Wear 1.0 and 2.0 watches.
|
||||
// https://developer.android.com/training/wearables/apps/packaging.html
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
applicationId "no.nordicsemi.android.nrftoolbox"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 28
|
||||
versionCode 282726903 // target: 28, version: 2.6.0, build: 68, multi-APK: 01
|
||||
versionName "2.7.2"
|
||||
resConfigs "en"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled true
|
||||
useProguard false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
useProguard true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
// exclude these from the build:
|
||||
configurations.all() { configuration -> exclude group: "org.apache.httpcomponents", module: "httpclient" }
|
||||
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha04'
|
||||
implementation 'androidx.percentlayout:percentlayout:1.0.0'
|
||||
implementation 'com.google.android.support:wearable:2.4.0'
|
||||
compileOnly 'com.google.android.wearable:wearable:2.4.0'
|
||||
|
||||
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.0'
|
||||
|
||||
// uncomment to enable the Wear UI Library
|
||||
// https://developer.android.com/training/wearables/ui/wear-ui-library.html
|
||||
// implementation 'com.android.support:wear:28.0.0'
|
||||
|
||||
// nRF Toolbox is using Play Service 10.2.0 in order to make the app working in China:
|
||||
// https://developer.android.com/training/wearables/apps/creating-app-china.html#ChinaSDK
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.google.android.gms:play-services-wearable:10.2.0'
|
||||
}
|
||||
17
wear/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Users\alno\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# 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 *;
|
||||
#}
|
||||
83
wear/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<manifest package="no.nordicsemi.android.nrftoolbox"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch"/>
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.DeviceDefault.Light"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.standalone"
|
||||
android:value="false" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
|
||||
<activity
|
||||
android:name=".ScannerActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".ble.BleProfileService" />
|
||||
<activity android:name=".uart.UARTConfigurationsActivity"
|
||||
android:launchMode="singleTop"/>
|
||||
<activity android:name=".uart.UARTCommandsActivity"
|
||||
android:launchMode="singleTop"/>
|
||||
|
||||
<!-- This receiver needs to be exported as it listens for notification broadcasts. -->
|
||||
<receiver android:name=".wearable.ActionReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="no.nordicsemi.android.nrftoolbox.ACTION_DISCONNECT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Service for handling Android Wear synchronization events. -->
|
||||
<service android:name=".wearable.MainWearableListenerService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
|
||||
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
|
||||
<data android:scheme="wear" android:host="*" android:pathPrefix="/nrftoolbox" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.support.wearable.view.CircledImageView;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class DeviceItemLayout extends RelativeLayout implements WearableListView.OnCenterProximityListener {
|
||||
private static final int ANIMATION_DURATION_MS = 150;
|
||||
/**
|
||||
* The ratio for the size of a circle in shrink state.
|
||||
*/
|
||||
private static final float SHRINK_CIRCLE_RATIO = .75f;
|
||||
|
||||
private static final float SHRINK_LABEL_ALPHA = .5f;
|
||||
private static final float EXPAND_LABEL_ALPHA = 1f;
|
||||
|
||||
private float mExpandCircleRadius;
|
||||
private float mShrinkCircleRadius;
|
||||
|
||||
private ObjectAnimator mExpandCircleAnimator;
|
||||
private ObjectAnimator mFadeInLabelAnimator;
|
||||
private AnimatorSet mExpandAnimator;
|
||||
|
||||
private ObjectAnimator mShrinkCircleAnimator;
|
||||
private ObjectAnimator mFadeOutLabelAnimator;
|
||||
private AnimatorSet mShrinkAnimator;
|
||||
|
||||
private TextView mName;
|
||||
private CircledImageView mIcon;
|
||||
|
||||
public DeviceItemLayout(final Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public DeviceItemLayout(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DeviceItemLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
mName = findViewById(R.id.name);
|
||||
mIcon = findViewById(R.id.icon);
|
||||
mExpandCircleRadius = mIcon.getCircleRadius();
|
||||
mShrinkCircleRadius = mExpandCircleRadius * SHRINK_CIRCLE_RATIO;
|
||||
|
||||
mShrinkCircleAnimator = ObjectAnimator.ofFloat(mIcon, "circleRadius", mExpandCircleRadius, mShrinkCircleRadius);
|
||||
mFadeOutLabelAnimator = ObjectAnimator.ofFloat(mName, "alpha", EXPAND_LABEL_ALPHA, SHRINK_LABEL_ALPHA);
|
||||
mShrinkAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
|
||||
mShrinkAnimator.playTogether(mShrinkCircleAnimator, mFadeOutLabelAnimator);
|
||||
|
||||
mExpandCircleAnimator = ObjectAnimator.ofFloat(mIcon, "circleRadius", mShrinkCircleRadius, mExpandCircleRadius);
|
||||
mFadeInLabelAnimator = ObjectAnimator.ofFloat(mName, "alpha", SHRINK_LABEL_ALPHA, EXPAND_LABEL_ALPHA);
|
||||
mExpandAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
|
||||
mExpandAnimator.playTogether(mExpandCircleAnimator, mFadeInLabelAnimator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCenterPosition(final boolean animate) {
|
||||
if (animate) {
|
||||
mShrinkAnimator.cancel();
|
||||
if (!mExpandAnimator.isRunning()) {
|
||||
mExpandCircleAnimator.setFloatValues(mIcon.getCircleRadius(), mExpandCircleRadius);
|
||||
mFadeInLabelAnimator.setFloatValues(mName.getAlpha(), EXPAND_LABEL_ALPHA);
|
||||
mExpandAnimator.start();
|
||||
}
|
||||
} else {
|
||||
mExpandAnimator.cancel();
|
||||
mIcon.setCircleRadius(mExpandCircleRadius);
|
||||
mName.setAlpha(EXPAND_LABEL_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonCenterPosition(final boolean animate) {
|
||||
if (animate) {
|
||||
mExpandAnimator.cancel();
|
||||
if (!mShrinkAnimator.isRunning()) {
|
||||
mShrinkCircleAnimator.setFloatValues(mIcon.getCircleRadius(), mShrinkCircleRadius);
|
||||
mFadeOutLabelAnimator.setFloatValues(mName.getAlpha(), SHRINK_LABEL_ALPHA);
|
||||
mShrinkAnimator.start();
|
||||
}
|
||||
} else {
|
||||
mShrinkAnimator.cancel();
|
||||
mIcon.setCircleRadius(mShrinkCircleRadius);
|
||||
mName.setAlpha(SHRINK_LABEL_ALPHA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.support.wearable.view.CircledImageView;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
|
||||
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
|
||||
import no.nordicsemi.android.support.v18.scanner.ScanResult;
|
||||
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
|
||||
|
||||
public class DevicesAdapter extends WearableListView.Adapter {
|
||||
private static final String TAG = "DevicesAdapter";
|
||||
|
||||
private final static long SCAN_DURATION = 5000;
|
||||
|
||||
private final List<BluetoothDevice> mDevices = new ArrayList<>();
|
||||
private final LayoutInflater mInflater;
|
||||
private final Handler mHandler;
|
||||
private final WearableListView mListView;
|
||||
private final String mNotAvailable;
|
||||
private final String mConnectingText;
|
||||
private final String mAvailableText;
|
||||
private final String mBondedText;
|
||||
private final String mBondingText;
|
||||
/** A position of a device that the activity is currently connecting to. */
|
||||
private int mConnectingPosition = -1;
|
||||
/** Flag set to true when scanner is active. */
|
||||
private boolean mScanning;
|
||||
|
||||
public DevicesAdapter(final WearableListView listView) {
|
||||
final Context context = listView.getContext();
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mNotAvailable = context.getString(R.string.not_available);
|
||||
mConnectingText = context.getString(R.string.state_connecting);
|
||||
mAvailableText = context.getString(R.string.devices_list_available);
|
||||
mBondedText = context.getString(R.string.devices_list_bonded);
|
||||
mBondingText = context.getString(R.string.devices_list_bonding);
|
||||
mListView = listView;
|
||||
mHandler = new Handler();
|
||||
|
||||
final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter != null)
|
||||
mDevices.addAll(bluetoothAdapter.getBondedDevices());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WearableListView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final int position) {
|
||||
return new ItemViewHolder(mInflater.inflate(R.layout.device_item, viewGroup, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final WearableListView.ViewHolder holder, final int position) {
|
||||
final ItemViewHolder viewHolder = (ItemViewHolder) holder;
|
||||
|
||||
if (position < mDevices.size()) {
|
||||
final BluetoothDevice device = mDevices.get(position);
|
||||
|
||||
viewHolder.mDevice = device;
|
||||
viewHolder.mName.setText(TextUtils.isEmpty(device.getName()) ? mNotAvailable : device.getName());
|
||||
viewHolder.mAddress.setText(getState(device, position));
|
||||
viewHolder.mIcon.showIndeterminateProgress(position == mConnectingPosition);
|
||||
} else {
|
||||
viewHolder.mDevice = null;
|
||||
viewHolder.mName.setText(mScanning ? R.string.devices_list_scanning : R.string.devices_list_start_scan);
|
||||
viewHolder.mAddress.setText(null);
|
||||
viewHolder.mIcon.showIndeterminateProgress(mScanning);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mDevices.size() + (mConnectingPosition == -1 ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setConnectingPosition(final int connectingPosition) {
|
||||
final int oldPosition = mConnectingPosition;
|
||||
this.mConnectingPosition = connectingPosition;
|
||||
if (connectingPosition >= 0) {
|
||||
// The "Scan for nearby device' item is removed
|
||||
notifyItemChanged(connectingPosition);
|
||||
notifyItemRemoved(mDevices.size());
|
||||
} else {
|
||||
if (oldPosition >= 0)
|
||||
notifyItemChanged(oldPosition);
|
||||
notifyItemInserted(mDevices.size());
|
||||
}
|
||||
}
|
||||
|
||||
public void startLeScan() {
|
||||
// Scanning is disabled when we are connecting or connected.
|
||||
if (mConnectingPosition >= 0)
|
||||
return;
|
||||
|
||||
if (mScanning) {
|
||||
// Extend scanning for some time more
|
||||
mHandler.removeCallbacks(mStopScanTask);
|
||||
mHandler.postDelayed(mStopScanTask, SCAN_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
|
||||
final ScanSettings settings = new ScanSettings.Builder().setReportDelay(1000).setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
|
||||
scanner.startScan(null, settings, mScanCallback);
|
||||
|
||||
// Setup timer that will stop scanning
|
||||
mHandler.postDelayed(mStopScanTask, SCAN_DURATION);
|
||||
mScanning = true;
|
||||
notifyItemChanged(mDevices.size());
|
||||
}
|
||||
|
||||
public void stopLeScan() {
|
||||
if (!mScanning)
|
||||
return;
|
||||
|
||||
final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
|
||||
scanner.stopScan(mScanCallback);
|
||||
|
||||
mHandler.removeCallbacks(mStopScanTask);
|
||||
mScanning = false;
|
||||
notifyItemChanged(mDevices.size());
|
||||
}
|
||||
|
||||
private String getState(final BluetoothDevice device, final int position) {
|
||||
if (mConnectingPosition == position)
|
||||
return mConnectingText;
|
||||
else if (device.getBondState() == BluetoothDevice.BOND_BONDED)
|
||||
return mBondedText;
|
||||
else if (device.getBondState() == BluetoothDevice.BOND_BONDING)
|
||||
return mBondingText;
|
||||
return mAvailableText;
|
||||
}
|
||||
|
||||
private Runnable mStopScanTask = this::stopLeScan;
|
||||
|
||||
private ScanCallback mScanCallback = new ScanCallback() {
|
||||
@Override
|
||||
public void onScanResult(final int callbackType, @NonNull final ScanResult result) {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatchScanResults(final List<ScanResult> results) {
|
||||
final int size = mDevices.size();
|
||||
for (final ScanResult result : results) {
|
||||
final BluetoothDevice device = result.getDevice();
|
||||
if (!mDevices.contains(device))
|
||||
mDevices.add(device);
|
||||
}
|
||||
if (size != mDevices.size()) {
|
||||
notifyItemRangeInserted(size, mDevices.size() - size);
|
||||
if (size == 0)
|
||||
mListView.scrollToPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanFailed(final int errorCode) {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
public static class ItemViewHolder extends WearableListView.ViewHolder {
|
||||
private CircledImageView mIcon;
|
||||
private TextView mName;
|
||||
private TextView mAddress;
|
||||
private BluetoothDevice mDevice;
|
||||
|
||||
public ItemViewHolder(final View itemView) {
|
||||
super(itemView);
|
||||
|
||||
mIcon = itemView.findViewById(R.id.icon);
|
||||
mName = itemView.findViewById(R.id.name);
|
||||
mAddress = itemView.findViewById(R.id.state);
|
||||
}
|
||||
|
||||
/** Returns the Bluetooth device for that holder, or null for "Scanning for nearby devices" row. */
|
||||
public BluetoothDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfileService;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.UARTConfigurationsActivity;
|
||||
|
||||
public class ScannerActivity extends Activity {
|
||||
private static final String TAG = "ScannerActivity";
|
||||
|
||||
private static final int PERMISSION_REQUEST_LOCATION = 1;
|
||||
|
||||
private DevicesAdapter mDeviceAdapter;
|
||||
private View mHeader;
|
||||
|
||||
private BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
switch (action) {
|
||||
case BleProfileService.BROADCAST_CONNECTION_STATE: {
|
||||
final int state = intent.getIntExtra(BleProfileService.EXTRA_CONNECTION_STATE, BleProfileService.STATE_DISCONNECTED);
|
||||
if (state == BleProfileService.STATE_DISCONNECTED)
|
||||
mDeviceAdapter.setConnectingPosition(-1);
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_DEVICE_READY: {
|
||||
final Intent activity = new Intent(ScannerActivity.this, UARTConfigurationsActivity.class);
|
||||
startActivity(activity);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_DEVICE_NOT_SUPPORTED: {
|
||||
Toast.makeText(ScannerActivity.this, R.string.devices_list_device_not_supported, Toast.LENGTH_SHORT).show();
|
||||
mDeviceAdapter.setConnectingPosition(-1);
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_ERROR: {
|
||||
final String message = intent.getStringExtra(BleProfileService.EXTRA_ERROR_MESSAGE);
|
||||
// final int errorCode = intent.getIntExtra(BleProfileService.EXTRA_ERROR_CODE, 0);
|
||||
Toast.makeText(ScannerActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
// TODO error handing
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_BOND_STATE: {
|
||||
mDeviceAdapter.notifyDataSetChanged(); // TODO check this. Bonding was never tested.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_list_with_header);
|
||||
|
||||
// Get the list component from the layout of the activity
|
||||
final WearableListView listView = findViewById(R.id.devices_list);
|
||||
listView.setAdapter(mDeviceAdapter = new DevicesAdapter(listView));
|
||||
listView.setClickListener(mOnRowClickListener);
|
||||
listView.addOnScrollListener(mOnScrollListener);
|
||||
|
||||
// The header will be moved as the list is scrolled
|
||||
mHeader = findViewById(R.id.header);
|
||||
|
||||
// Register a broadcast receiver that will listen for events from the service.
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceBroadcastReceiver, BleProfileService.makeIntentFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceBroadcastReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case PERMISSION_REQUEST_LOCATION:
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
mDeviceAdapter.startLeScan();
|
||||
} else {
|
||||
Toast.makeText(ScannerActivity.this, "Location permission required", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_LOCATION);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mDeviceAdapter.startLeScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mDeviceAdapter.stopLeScan();
|
||||
}
|
||||
|
||||
/** List click listener. */
|
||||
private WearableListView.ClickListener mOnRowClickListener = new WearableListView.ClickListener() {
|
||||
@Override
|
||||
public void onClick(final WearableListView.ViewHolder holder) {
|
||||
final DevicesAdapter.ItemViewHolder viewHolder = (DevicesAdapter.ItemViewHolder) holder;
|
||||
final BluetoothDevice device = viewHolder.getDevice();
|
||||
|
||||
if (device != null) {
|
||||
mDeviceAdapter.stopLeScan();
|
||||
mDeviceAdapter.setConnectingPosition(holder.getAdapterPosition());
|
||||
|
||||
// Start the service that will connect to selected device
|
||||
final Intent service = new Intent(ScannerActivity.this, BleProfileService.class);
|
||||
service.putExtra(BleProfileService.EXTRA_DEVICE_ADDRESS, device.getAddress());
|
||||
startService(service);
|
||||
} else {
|
||||
mDeviceAdapter.startLeScan();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTopEmptyRegionClick() {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/** The following code ensures that the title scrolls as the user scrolls up or down the list/ */
|
||||
private WearableListView.OnScrollListener mOnScrollListener = new WearableListView.OnScrollListener() {
|
||||
@Override
|
||||
public void onAbsoluteScrollChange(final int i) {
|
||||
if (i > 0)
|
||||
mHeader.setY(-i);
|
||||
else
|
||||
mHeader.setY(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(final int i) {
|
||||
// Placeholder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(final int i) {
|
||||
// Placeholder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCentralPositionChanged(final int i) {
|
||||
// Placeholder
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
|
||||
public interface BleManagerCallbacks {
|
||||
|
||||
/**
|
||||
* Called when the Android device started connecting to given device.
|
||||
* The {@link #onDeviceConnected(BluetoothDevice)} will be called when the device is connected,
|
||||
* or {@link #onError(BluetoothDevice, String, int)} in case of error.
|
||||
* @param device the device that got connected
|
||||
*/
|
||||
void onDeviceConnecting(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has been connected. This does not mean that the application may start communication.
|
||||
* A service discovery will be handled automatically after this call. Service discovery
|
||||
* may ends up with calling {@link #onDeviceReady(BluetoothDevice)}
|
||||
* or {@link #onDeviceNotSupported(BluetoothDevice)} if required services have not been found.
|
||||
* @param device target device
|
||||
*/
|
||||
void onDeviceConnected(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Method called when all initialization requests has been completed.
|
||||
* @param device target device
|
||||
*/
|
||||
void onDeviceReady(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* This method should return true if Battery Level notifications should be enabled on the target device.
|
||||
* If there is no Battery Service, or the Battery Level characteristic does not have NOTIFY property,
|
||||
* this method will not be called for this device.
|
||||
* <p>This method may return true only if an activity is bound to the service (to display the information
|
||||
* to the user), always (e.g. if critical battery level is reported using notifications) or never, if
|
||||
* such information is not important or the manager wants to control Battery Level notifications on its own.</p>
|
||||
* @param device target device
|
||||
* @return true to enabled battery level notifications after connecting to the device, false otherwise
|
||||
*/
|
||||
boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when user initialized disconnection.
|
||||
* @param device target device
|
||||
*/
|
||||
void onDeviceDisconnecting(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has disconnected (when the callback returned
|
||||
* {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state DISCONNECTED),
|
||||
* but ONLY if the {@link BleManager#shouldAutoConnect()} method returned false for this device when it was connecting.
|
||||
* Otherwise the {@link #onLinklossOccurred(BluetoothDevice)} method will be called instead.
|
||||
* @param device the device that got disconnected
|
||||
*/
|
||||
void onDeviceDisconnected(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* This callback is invoked when the Ble Manager lost connection to a device that has been connected with autoConnect option.
|
||||
* Otherwise a {@link #onDeviceDisconnected(BluetoothDevice)} method will be called on such event.
|
||||
* @param device target device
|
||||
*/
|
||||
void onLinklossOccurred(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when an {@link BluetoothGatt#GATT_INSUFFICIENT_AUTHENTICATION} error occurred and the device bond state is NOT_BONDED
|
||||
* @param device target device
|
||||
*/
|
||||
void onBondingRequired(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when the device has been successfully bonded.
|
||||
* @param device target device
|
||||
*/
|
||||
void onBonded(final BluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Called when a BLE error has occurred
|
||||
*
|
||||
* @param device target device
|
||||
* @param message the error message
|
||||
* @param errorCode the error code
|
||||
*/
|
||||
void onError(final BluetoothDevice device, final String message, final int errorCode);
|
||||
|
||||
/**
|
||||
* Called when service discovery has finished but the main services were not found on the device.
|
||||
* @param device target device
|
||||
*/
|
||||
void onDeviceNotSupported(final BluetoothDevice device);
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.Deque;
|
||||
|
||||
public abstract class BleProfile {
|
||||
private Context mContext;
|
||||
private BleProfileApi mApi;
|
||||
|
||||
/* package */ void setApi(final BleProfileApi api) {
|
||||
this.mContext = api.getContext();
|
||||
this.mApi = api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BLE API for sending data to the remote device.
|
||||
*/
|
||||
public BleProfileApi getApi() {
|
||||
return mApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the service context.
|
||||
* @return the context
|
||||
*/
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of requests needed to initialize the profile.
|
||||
* Enabling Service Change indications for bonded devices and reading the Battery Level value and enabling Battery Level notifications
|
||||
* is handled before executing this queue. The queue should not have requests that are not available, e.g. should not
|
||||
* read an optional service when it is not supported by the connected device.
|
||||
* <p>This method is called when the services has been discovered and the device is supported (has required service).</p>
|
||||
*
|
||||
* @param gatt the gatt device with services discovered
|
||||
* @return the queue of requests
|
||||
*/
|
||||
protected abstract Deque<BleManager.Request> initGatt(final BluetoothGatt gatt);
|
||||
|
||||
/**
|
||||
* Releases all profile resources. The device is no longer connected.
|
||||
*/
|
||||
protected abstract void release();
|
||||
|
||||
/**
|
||||
* Called when battery value has been received from the device.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param value the battery value in percent
|
||||
*/
|
||||
protected void onBatteryValueReceived(final BluetoothGatt gatt, final int value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback reporting the result of a characteristic read operation.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic that was read from the associated remote device.
|
||||
*/
|
||||
protected void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating the result of a characteristic write operation.
|
||||
* <p>If this callback is invoked while a reliable write transaction is
|
||||
* in progress, the value of the characteristic represents the value
|
||||
* reported by the remote device. An application should compare this
|
||||
* value to the desired value to be written. If the values don't match,
|
||||
* the application must abort the reliable write transaction.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic that was written to the associated remote device.
|
||||
*/
|
||||
protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback reporting the result of a descriptor read operation.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param descriptor Descriptor that was read from the associated remote device.
|
||||
*/
|
||||
protected void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating the result of a descriptor write operation.
|
||||
* <p>If this callback is invoked while a reliable write transaction is in progress,
|
||||
* the value of the characteristic represents the value reported by the remote device.
|
||||
* An application should compare this value to the desired value to be written.
|
||||
* If the values don't match, the application must abort the reliable write transaction.
|
||||
*
|
||||
* @param gatt GATT client
|
||||
* @param descriptor Descriptor that was written to the associated remote device.
|
||||
*/
|
||||
protected void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating a notification has been received.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic from which the notification came.
|
||||
*/
|
||||
protected void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating an indication has been received.
|
||||
* @param gatt GATT client
|
||||
* @param characteristic Characteristic from which the indication came.
|
||||
*/
|
||||
protected void onCharacteristicIndicated(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when the MTU request has finished with success. The MTU value may
|
||||
* be different than requested one.
|
||||
* @param mtu the new MTU (Maximum Transfer Unit)
|
||||
*/
|
||||
protected void onMtuChanged(final int mtu) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback indicating the connection parameters were updated. Works on Android 8+.
|
||||
*
|
||||
* @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
|
||||
* 6 (7.5ms) to 3200 (4000ms).
|
||||
* @param latency Slave latency for the connection in number of connection events. Valid range
|
||||
* is from 0 to 499
|
||||
* @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
|
||||
* (0.1s) to 3200 (32s)
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
protected void onConnectionUpdated(final int interval, final int latency, final int timeout) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a BLE error has occurred
|
||||
* @param message the error message
|
||||
* @param errorCode the error code
|
||||
*/
|
||||
public void onError(final String message, final int errorCode) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
|
||||
public interface BleProfileApi {
|
||||
|
||||
/**
|
||||
* On Android, when multiple BLE operations needs to be done, it is required to wait for a proper
|
||||
* {@link android.bluetooth.BluetoothGattCallback BluetoothGattCallback} callback before calling
|
||||
* another operation. In order to make BLE operations easier the BleManager allows to enqueue a request
|
||||
* containing all data necessary for a given operation. Requests are performed one after another until the
|
||||
* queue is empty. Use static methods from below to instantiate a request and then enqueue them using {@link #enqueue(Request)}.
|
||||
*/
|
||||
final class Request {
|
||||
enum Type {
|
||||
CREATE_BOND,
|
||||
WRITE,
|
||||
READ,
|
||||
WRITE_DESCRIPTOR,
|
||||
READ_DESCRIPTOR,
|
||||
ENABLE_NOTIFICATIONS,
|
||||
ENABLE_INDICATIONS,
|
||||
READ_BATTERY_LEVEL,
|
||||
ENABLE_BATTERY_LEVEL_NOTIFICATIONS,
|
||||
DISABLE_BATTERY_LEVEL_NOTIFICATIONS,
|
||||
ENABLE_SERVICE_CHANGED_INDICATIONS,
|
||||
REQUEST_MTU,
|
||||
REQUEST_CONNECTION_PRIORITY,
|
||||
}
|
||||
|
||||
final Type type;
|
||||
final BluetoothGattCharacteristic characteristic;
|
||||
final BluetoothGattDescriptor descriptor;
|
||||
final byte[] data;
|
||||
final int writeType;
|
||||
final int value;
|
||||
|
||||
private Request(final Type type) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = null;
|
||||
this.data = null;
|
||||
this.writeType = 0;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final int value) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = null;
|
||||
this.data = null;
|
||||
this.writeType = 0;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.descriptor = null;
|
||||
this.data = null;
|
||||
this.writeType = 0;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattCharacteristic characteristic, final int writeType, final byte[] data, final int offset, final int length) {
|
||||
this.type = type;
|
||||
this.characteristic = characteristic;
|
||||
this.descriptor = null;
|
||||
this.data = copy(data, offset, length);
|
||||
this.writeType = writeType;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattDescriptor descriptor) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = descriptor;
|
||||
this.data = null;
|
||||
this.writeType = 0;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
private Request(final Type type, final BluetoothGattDescriptor descriptor, final byte[] data, final int offset, final int length) {
|
||||
this.type = type;
|
||||
this.characteristic = null;
|
||||
this.descriptor = descriptor;
|
||||
this.data = copy(data, offset, length);
|
||||
this.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
private static byte[] copy(final byte[] value, final int offset, final int length) {
|
||||
if (value == null || offset > value.length)
|
||||
return null;
|
||||
final int maxLength = Math.min(value.length - offset, length);
|
||||
final byte[] copy = new byte[maxLength];
|
||||
System.arraycopy(value, offset, copy, 0, maxLength);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request that will start pairing with the device.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request createBond() {
|
||||
return new Request(Type.CREATE_BOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Read Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have READ property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be read
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.READ, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] data) {
|
||||
return new Request(Type.WRITE, characteristic, characteristic.getWriteType(), data, 0, data != null ? data.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param writeType write type to be used, one of {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT}, {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE}.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] data, final int writeType) {
|
||||
return new Request(Type.WRITE, characteristic, writeType, data, 0, data != null ? data.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which data has to be copied
|
||||
* @param length number of bytes to be copied from the data buffer
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] data, final int offset, final int length) {
|
||||
return new Request(Type.WRITE, characteristic, characteristic.getWriteType(), data, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Characteristic request. The request will not be executed if given characteristic
|
||||
* is null or does not have WRITE property. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which data has to be copied
|
||||
* @param length number of bytes to be copied from the data buffer
|
||||
* @param writeType write type to be used, one of {@link BluetoothGattCharacteristic#WRITE_TYPE_DEFAULT}, {@link BluetoothGattCharacteristic#WRITE_TYPE_NO_RESPONSE}.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, final byte[] data, final int offset, final int length, final int writeType) {
|
||||
return new Request(Type.WRITE, characteristic, writeType, data, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Read Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be read
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadRequest(final BluetoothGattDescriptor descriptor) {
|
||||
return new Request(Type.READ_DESCRIPTOR, descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattDescriptor descriptor, final byte[] data) {
|
||||
return new Request(Type.WRITE_DESCRIPTOR, descriptor, data, 0, data != null ? data.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Write Descriptor request. The request will not be executed if given descriptor
|
||||
* is null. After the operation is complete a proper callback will be invoked.
|
||||
* @param descriptor descriptor to be written
|
||||
* @param data data to be written. The array is copied into another buffer so it's safe to reuse the array again.
|
||||
* @param offset the offset from which data has to be copied
|
||||
* @param length number of bytes to be copied from the data buffer
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newWriteRequest(final BluetoothGattDescriptor descriptor, final byte[] data, final int offset, final int length) {
|
||||
return new Request(Type.WRITE_DESCRIPTOR, descriptor, data, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Enable Notification request. The request will not be executed if given characteristic
|
||||
* is null, does not have NOTIFY property or the CCCD. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to have notifications enabled
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableNotificationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_NOTIFICATIONS, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new Enable Indications request. The request will not be executed if given characteristic
|
||||
* is null, does not have INDICATE property or the CCCD. After the operation is complete a proper callback will be invoked.
|
||||
* @param characteristic characteristic to have indications enabled
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableIndicationsRequest(final BluetoothGattCharacteristic characteristic) {
|
||||
return new Request(Type.ENABLE_INDICATIONS, characteristic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first found Battery Level characteristic value from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the READ property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newReadBatteryLevelRequest() {
|
||||
return new Request(Type.READ_BATTERY_LEVEL); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables notifications on the first found Battery Level characteristic from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the NOTIFY property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newEnableBatteryLevelNotificationsRequest() {
|
||||
return new Request(Type.ENABLE_BATTERY_LEVEL_NOTIFICATIONS); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables notifications on the first found Battery Level characteristic from the first found Battery Service.
|
||||
* If any of them is not found, or the characteristic does not have the NOTIFY property this operation will not execute.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
public static Request newDisableBatteryLevelNotificationsRequest() {
|
||||
return new Request(Type.DISABLE_BATTERY_LEVEL_NOTIFICATIONS); // the first Battery Level char from the first Battery Service is used
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables indications on Service Changed characteristic if such exists in the Generic Attribute service.
|
||||
* It is required to enable those notifications on bonded devices on older Android versions to be
|
||||
* informed about attributes changes. Android 7+ (or 6+) handles this automatically and no action is required.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
static Request newEnableServiceChangedIndicationsRequest() {
|
||||
return new Request(Type.ENABLE_SERVICE_CHANGED_INDICATIONS); // the only Service Changed char is used (if such exists)
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests new MTU (Maximum Transfer Unit). This is only supported on Android Lollipop or newer.
|
||||
* The target device may reject requested data and set smalled MTU.
|
||||
* @param mtu the new MTU. Acceptable values are <23, 517>.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
static Request newMtuRequest(int mtu) {
|
||||
if (mtu < 23)
|
||||
mtu = 23;
|
||||
if (mtu > 517)
|
||||
mtu = 517;
|
||||
return new Request(Type.REQUEST_MTU, mtu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the new connection priority. Acceptable values are:
|
||||
* <ol>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} - Interval: 11.25 -15 ms, latency: 0, supervision timeout: 20 sec,</li>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} - Interval: 30 - 50 ms, latency: 0, supervision timeout: 20 sec,</li>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER} - Interval: 100 - 125 ms, latency: 2, supervision timeout: 20 sec.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param priority one of: {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}, {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
|
||||
* {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
|
||||
* @return the new request that can be enqueued using {@link #enqueue(Request)} method.
|
||||
*/
|
||||
static Request newConnectionPriorityRequest(int priority) {
|
||||
if (priority < 0 || priority > 2)
|
||||
priority = 0; // Balanced
|
||||
return new Request(Type.REQUEST_CONNECTION_PRIORITY, priority);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context.
|
||||
*/
|
||||
Context getContext();
|
||||
|
||||
/**
|
||||
* Enqueues creating bond request to the queue.
|
||||
* @return true if request has been enqueued, false if the device has not been connected
|
||||
*/
|
||||
boolean createBond();
|
||||
|
||||
/**
|
||||
* Enables notifications on given characteristic
|
||||
*
|
||||
* @return true is the request has been enqueued
|
||||
*/
|
||||
boolean enableNotifications(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Enables indications on given characteristic
|
||||
*
|
||||
* @return true is the request has been enqueued
|
||||
*/
|
||||
boolean enableIndications(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Sends the read request to the given characteristic.
|
||||
*
|
||||
* @param characteristic the characteristic to read
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean readCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Writes the characteristic value to the given characteristic.
|
||||
*
|
||||
* @param characteristic the characteristic to write to
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean writeCharacteristic(final BluetoothGattCharacteristic characteristic);
|
||||
|
||||
/**
|
||||
* Sends the read request to the given descriptor.
|
||||
*
|
||||
* @param descriptor the descriptor to read
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean readDescriptor(final BluetoothGattDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Writes the descriptor value to the given descriptor.
|
||||
*
|
||||
* @param descriptor the descriptor to write to
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean writeDescriptor(final BluetoothGattDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Reads the battery level from the device.
|
||||
*
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean readBatteryLevel();
|
||||
|
||||
/**
|
||||
* This method tries to enable notifications on the Battery Level characteristic.
|
||||
*
|
||||
* @param enable <code>true</code> to enable battery notifications, false to disable
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean setBatteryNotifications(final boolean enable);
|
||||
|
||||
/**
|
||||
* Requests new MTU. On Android 4.3 and 4.4.x returns false.
|
||||
*
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean requestMtu(final int mtu);
|
||||
|
||||
/**
|
||||
* Returns the current MTU (Maximum Transfer Unit). MTU specifies the maximum number of bytes that can
|
||||
* be sent in a single write operation. 3 bytes are used for internal purposes, so the maximum size is MTU-3.
|
||||
* The value will changed only if requested with {@link #requestMtu(int)} and a successful callback is received.
|
||||
* If the peripheral requests MTU change, the {@link BluetoothGattCallback#onMtuChanged(BluetoothGatt, int, int)}
|
||||
* callback is not invoked, therefor the returned MTU value will not be correct.
|
||||
* Use {@link android.bluetooth.BluetoothGattServerCallback#onMtuChanged(BluetoothDevice, int)} to get the
|
||||
* callback with right value requested from the peripheral side.
|
||||
* @return the current MTU value. Default to 23.
|
||||
*/
|
||||
int getMtu();
|
||||
|
||||
/**
|
||||
* This method overrides the MTU value. Use it only when the peripheral has changed MTU and you
|
||||
* received the {@link android.bluetooth.BluetoothGattServerCallback#onMtuChanged(BluetoothDevice, int)}
|
||||
* callback. If you want to set MTU as a master, use {@link #requestMtu(int)} instead.
|
||||
* @param mtu the MTU value set by the peripheral.
|
||||
*/
|
||||
void overrideMtu(final int mtu);
|
||||
|
||||
/**
|
||||
* Requests the new connection priority. Acceptable values are:
|
||||
* <ol>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} - Interval: 11.25 -15 ms, latency: 0, supervision timeout: 20 sec,</li>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} - Interval: 30 - 50 ms, latency: 0, supervision timeout: 20 sec,</li>
|
||||
* <li>{@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER} - Interval: 100 - 125 ms, latency: 2, supervision timeout: 20 sec.</li>
|
||||
* </ol>
|
||||
* On Android 4.3 and 4.4.x returns false.
|
||||
*
|
||||
* @param priority one of: {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}, {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
|
||||
* {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
|
||||
* @return true if request has been enqueued
|
||||
*/
|
||||
boolean requestConnectionPriority(final int priority);
|
||||
|
||||
/**
|
||||
* Enqueues a new request. The request will be handled immediately if there is no operation in progress,
|
||||
* or automatically after the last enqueued one will finish.
|
||||
* <p>This method should be used to read and write data from the target device as it ensures that the last operation has finished
|
||||
* before a new one will be called.</p>
|
||||
* @param request new request to be performed
|
||||
* @return true if request has been enqueued, false if the device is not connected
|
||||
*/
|
||||
boolean enqueue(final Request request);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.uart.UARTProfile;
|
||||
|
||||
public class BleProfileProvider {
|
||||
|
||||
public static BleProfile findProfile(final BluetoothGatt gatt) {
|
||||
if (UARTProfile.matchDevice(gatt))
|
||||
return new UARTProfile();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package no.nordicsemi.android.nrftoolbox.ble;
|
||||
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class BleProfileService extends Service implements BleManagerCallbacks {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "BleProfileService";
|
||||
|
||||
public static final String BROADCAST_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_CONNECTION_STATE";
|
||||
public static final String BROADCAST_DEVICE_NOT_SUPPORTED = "no.nordicsemi.android.nrftoolbox.BROADCAST_DEVICE_NOT_SUPPORTED";
|
||||
public static final String BROADCAST_DEVICE_READY = "no.nordicsemi.android.nrftoolbox.DEVICE_READY";
|
||||
public static final String BROADCAST_BOND_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_BOND_STATE";
|
||||
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
|
||||
public static final String BROADCAST_ERROR = "no.nordicsemi.android.nrftoolbox.BROADCAST_ERROR";
|
||||
|
||||
/** The parameter passed when creating the service. Must contain the address of the sensor that we want to connect to */
|
||||
public static final String EXTRA_DEVICE_ADDRESS = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_ADDRESS";
|
||||
/** The key for the device name that is returned in {@link #BROADCAST_CONNECTION_STATE} with state {@link #STATE_CONNECTED}. */
|
||||
public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_NAME";
|
||||
public static final String EXTRA_DEVICE = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE";
|
||||
public static final String EXTRA_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_CONNECTION_STATE";
|
||||
public static final String EXTRA_BOND_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_BOND_STATE";
|
||||
public static final String EXTRA_ERROR_MESSAGE = "no.nordicsemi.android.nrftoolbox.EXTRA_ERROR_MESSAGE";
|
||||
public static final String EXTRA_ERROR_CODE = "no.nordicsemi.android.nrftoolbox.EXTRA_ERROR_CODE";
|
||||
|
||||
public static final int STATE_LINK_LOSS = -1;
|
||||
public static final int STATE_DISCONNECTED = 0;
|
||||
public static final int STATE_CONNECTED = 1;
|
||||
public static final int STATE_CONNECTING = 2;
|
||||
public static final int STATE_DISCONNECTING = 3;
|
||||
|
||||
private BleManager mBleManager;
|
||||
private Handler mHandler;
|
||||
|
||||
protected boolean mBound;
|
||||
private boolean mConnected;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private String mDeviceName;
|
||||
|
||||
private final BroadcastReceiver mBluetoothStateBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
||||
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
onBluetoothEnabled();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
onBluetoothDisabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
/**
|
||||
* Disconnects from the sensor.
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (!mConnected) {
|
||||
mBleManager.close();
|
||||
onDeviceDisconnected(mBluetoothDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
mBleManager.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device address
|
||||
*
|
||||
* @return device address
|
||||
*/
|
||||
public final String getDeviceAddress() {
|
||||
return mBluetoothDevice.getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device name
|
||||
*
|
||||
* @return the device name
|
||||
*/
|
||||
public final String getDeviceName() {
|
||||
return mDeviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Bluetooth device
|
||||
*
|
||||
* @return the Bluetooth device
|
||||
*/
|
||||
public final BluetoothDevice getBluetoothDevice() {
|
||||
return mBluetoothDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected to the sensor.
|
||||
*
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
public final boolean isConnected() {
|
||||
return mConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Profile API. Profile may be null if service discovery has not been performed or the device does not match any profile.
|
||||
*/
|
||||
public final BleProfile getProfile() {
|
||||
return mBleManager.getProfile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the bound activity.
|
||||
*
|
||||
* @return the service binder
|
||||
*/
|
||||
protected LocalBinder getBinder() {
|
||||
// default implementation returns the basic binder. You can overwrite the LocalBinder with your own, wider implementation
|
||||
return new LocalBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(final Intent intent) {
|
||||
mBound = true;
|
||||
return getBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onRebind(final Intent intent) {
|
||||
mBound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onUnbind(final Intent intent) {
|
||||
mBound = false;
|
||||
|
||||
// We want the onRebind method be called if anything else binds to it again
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
mHandler = new Handler();
|
||||
|
||||
// initialize the manager
|
||||
mBleManager = new BleManager(this, this);
|
||||
|
||||
// Register broadcast receivers
|
||||
registerReceiver(mBluetoothStateBroadcastReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
|
||||
// Service has now been created
|
||||
onServiceCreated();
|
||||
|
||||
// Call onBluetoothEnabled if Bluetooth enabled
|
||||
final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter.isEnabled()) {
|
||||
onBluetoothEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been created, before the {@link #onBluetoothEnabled()} is called.
|
||||
*/
|
||||
protected void onServiceCreated() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
if (intent == null || !intent.hasExtra(EXTRA_DEVICE_ADDRESS))
|
||||
throw new UnsupportedOperationException("No device address at EXTRA_DEVICE_ADDRESS key");
|
||||
|
||||
mDeviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
|
||||
|
||||
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
|
||||
final BluetoothAdapter adapter = bluetoothManager.getAdapter();
|
||||
final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
|
||||
mBluetoothDevice = adapter.getRemoteDevice(deviceAddress);
|
||||
onServiceStarted();
|
||||
|
||||
mBleManager.connect(mBluetoothDevice);
|
||||
return START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been started. The device name and address are set. It nRF Logger is installed than logger was also initialized.
|
||||
*/
|
||||
protected void onServiceStarted() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Unregister broadcast receivers
|
||||
unregisterReceiver(mBluetoothStateBroadcastReceiver);
|
||||
|
||||
// shutdown the manager
|
||||
mBleManager.close();
|
||||
mBleManager = null;
|
||||
mBluetoothDevice = null;
|
||||
mDeviceName = null;
|
||||
mConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when Bluetooth Adapter has been disabled.
|
||||
*/
|
||||
protected void onBluetoothDisabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Bluetooth Adapter has been enabled and
|
||||
* after the service was created if Bluetooth Adapter was enabled at that moment.
|
||||
* This method could initialize all Bluetooth related features, for example open the GATT server.
|
||||
*/
|
||||
protected void onBluetoothEnabled() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// By default the Battery Level notifications will be enabled only the activity is bound.
|
||||
return mBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnecting(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnected(final BluetoothDevice device) {
|
||||
mConnected = true;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_DEVICE_NAME, mDeviceName);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnecting(final BluetoothDevice device) {
|
||||
// Notify user about changing the state to DISCONNECTING
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnected(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinklossOccurred(final BluetoothDevice device) {
|
||||
mConnected = false;
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_DEVICE_READY);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_DEVICE_NOT_SUPPORTED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// no need for disconnecting, it will be disconnected by the manager automatically
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBondingRequired(final BluetoothDevice device) {
|
||||
showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonding);
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBonded(final BluetoothDevice device) {
|
||||
showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonded);
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
final Intent broadcast = new Intent(BROADCAST_ERROR);
|
||||
broadcast.putExtra(EXTRA_DEVICE, mBluetoothDevice);
|
||||
broadcast.putExtra(EXTRA_ERROR_MESSAGE, message);
|
||||
broadcast.putExtra(EXTRA_ERROR_CODE, errorCode);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// After receiving an error the device will be automatically disconnected.
|
||||
// Replace it with other implementation if necessary.
|
||||
mBleManager.disconnect();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
* @param messageResId
|
||||
* an resource id of the message to be shown
|
||||
*/
|
||||
private void showToast(final int messageResId) {
|
||||
mHandler.post(() -> Toast.makeText(BleProfileService.this, messageResId, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent filter that filters for all broadcast events sent by this service.
|
||||
*/
|
||||
public static IntentFilter makeIntentFilter() {
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BROADCAST_CONNECTION_STATE);
|
||||
filter.addAction(BROADCAST_BOND_STATE);
|
||||
filter.addAction(BROADCAST_DEVICE_READY);
|
||||
filter.addAction(BROADCAST_DEVICE_NOT_SUPPORTED);
|
||||
filter.addAction(BROADCAST_ERROR);
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.support.wearable.view.DotsPageIndicator;
|
||||
import android.support.wearable.view.GridViewPager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.wearable.DataApi;
|
||||
import com.google.android.gms.wearable.DataEvent;
|
||||
import com.google.android.gms.wearable.DataEventBuffer;
|
||||
import com.google.android.gms.wearable.DataItem;
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
import com.google.android.gms.wearable.DataMapItem;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.NodeApi;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfileService;
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.Command;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.UartConfiguration;
|
||||
|
||||
public class UARTCommandsActivity extends Activity implements UARTCommandsAdapter.OnCommandSelectedListener, GoogleApiClient.ConnectionCallbacks,
|
||||
DataApi.DataListener, GoogleApiClient.OnConnectionFailedListener, MessageApi.MessageListener {
|
||||
private static final String TAG = "UARTCommandsActivity";
|
||||
|
||||
public static final String CONFIGURATION = "configuration";
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private UARTCommandsAdapter mAdapter;
|
||||
private UARTProfile mProfile;
|
||||
private long mConfigurationId;
|
||||
|
||||
private BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
switch (action) {
|
||||
case BleProfileService.BROADCAST_CONNECTION_STATE: {
|
||||
final int state = intent.getIntExtra(BleProfileService.EXTRA_CONNECTION_STATE, BleProfileService.STATE_DISCONNECTED);
|
||||
if (state == BleProfileService.STATE_DISCONNECTED)
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_ERROR: {
|
||||
final String message = intent.getStringExtra(BleProfileService.EXTRA_ERROR_MESSAGE);
|
||||
// final int errorCode = intent.getIntExtra(BleProfileService.EXTRA_ERROR_CODE, 0);
|
||||
Toast.makeText(UARTCommandsActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
// TODO error handing
|
||||
break;
|
||||
}
|
||||
case UARTProfile.BROADCAST_DATA_RECEIVED: {
|
||||
// Here we could have shown the incoming message somehow.
|
||||
// However, notifications on TX characteristics are not enabled so this does not have to be implemented.
|
||||
// final String message = intent.getStringExtra(UARTProfile.EXTRA_DATA);
|
||||
// Toast.makeText(UARTCommandsActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName name, final IBinder service) {
|
||||
final BleProfileService.LocalBinder binder = (BleProfileService.LocalBinder) service;
|
||||
mProfile = (UARTProfile) binder.getProfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
mProfile = null;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_grid_pager);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
final UartConfiguration configuration = intent.getParcelableExtra(CONFIGURATION);
|
||||
mConfigurationId = configuration.getId();
|
||||
|
||||
// Check if the WEAR device is connected to the UART device itself, or by the phone.
|
||||
// Binding will fail if we are using phone as proxy as the service has not been started before.
|
||||
final Intent service = new Intent(this, BleProfileService.class);
|
||||
bindService(service, mServiceConnection, 0);
|
||||
|
||||
// Set up tht grid
|
||||
final GridViewPager pager = findViewById(R.id.pager);
|
||||
pager.setAdapter(mAdapter = new UARTCommandsAdapter(configuration, this));
|
||||
|
||||
final DotsPageIndicator dotsPageIndicator = findViewById(R.id.page_indicator);
|
||||
dotsPageIndicator.setPager(pager);
|
||||
|
||||
// Configure Google API client
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.build();
|
||||
|
||||
// Register the broadcast receiver that will listen for events from the device
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BleProfileService.BROADCAST_CONNECTION_STATE);
|
||||
filter.addAction(BleProfileService.BROADCAST_ERROR);
|
||||
filter.addAction(UARTProfile.BROADCAST_DATA_RECEIVED);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceBroadcastReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mGoogleApiClient.unregisterConnectionCallbacks(this);
|
||||
mGoogleApiClient.unregisterConnectionFailedListener(this);
|
||||
mGoogleApiClient = null;
|
||||
|
||||
// unbind if we were bound to the service.
|
||||
unbindService(mServiceConnection);
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceBroadcastReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mGoogleApiClient.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
|
||||
Wearable.DataApi.removeListener(mGoogleApiClient, this);
|
||||
mGoogleApiClient.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(final Bundle bundle) {
|
||||
Wearable.DataApi.addListener(mGoogleApiClient, this);
|
||||
Wearable.MessageApi.addListener(mGoogleApiClient, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(final int cause) {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChanged(final DataEventBuffer dataEventBuffer) {
|
||||
for (final DataEvent event : dataEventBuffer) {
|
||||
final DataItem item = event.getDataItem();
|
||||
final long id = ContentUris.parseId(item.getUri());
|
||||
|
||||
// Update the configuration only if ID matches
|
||||
if (id != mConfigurationId)
|
||||
continue;
|
||||
|
||||
// Configuration added or edited
|
||||
if (event.getType() == DataEvent.TYPE_CHANGED) {
|
||||
final DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
|
||||
final UartConfiguration configuration = new UartConfiguration(dataMap, id);
|
||||
|
||||
// Update UI on UI thread
|
||||
runOnUiThread(() -> mAdapter.setConfiguration(configuration));
|
||||
} else if (event.getType() == DataEvent.TYPE_DELETED) {
|
||||
// Configuration removed
|
||||
|
||||
// Update UI on UI thread
|
||||
runOnUiThread(() -> mAdapter.setConfiguration(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(final MessageEvent messageEvent) {
|
||||
// If the activity is bound to service it means that it has connected directly to the device. We ignore messages from the handheld.
|
||||
if (mProfile != null)
|
||||
return;
|
||||
|
||||
switch (messageEvent.getPath()) {
|
||||
case Constants.UART.DEVICE_LINKLOSS:
|
||||
case Constants.UART.DEVICE_DISCONNECTED: {
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandSelected(final Command command) {
|
||||
// Send command to handheld if the watch is not connected directly to the UART device.
|
||||
final Command.Eol eol = command.getEol();
|
||||
String text = command.getCommand();
|
||||
switch (eol) {
|
||||
case CR_LF:
|
||||
text = text.replaceAll("\n", "\r\n");
|
||||
break;
|
||||
case CR:
|
||||
text = text.replaceAll("\n", "\r");
|
||||
break;
|
||||
}
|
||||
|
||||
if (mProfile != null)
|
||||
mProfile.send(text);
|
||||
else
|
||||
sendMessageToHandheld(this, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given command to the handheld.
|
||||
*
|
||||
* @param command the message
|
||||
*/
|
||||
private void sendMessageToHandheld(final @NonNull Context context, final @NonNull String command) {
|
||||
new Thread(() -> {
|
||||
final GoogleApiClient client = new GoogleApiClient.Builder(context)
|
||||
.addApi(Wearable.API)
|
||||
.build();
|
||||
client.blockingConnect();
|
||||
|
||||
final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(client).await();
|
||||
for (Node node : nodes.getNodes()) {
|
||||
final MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(client, node.getId(), Constants.UART.COMMAND, command.getBytes()).await();
|
||||
if (!result.getStatus().isSuccess()) {
|
||||
Log.w(TAG, "Failed to send " + Constants.UART.COMMAND + " to " + node.getDisplayName());
|
||||
}
|
||||
}
|
||||
client.disconnect();
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.support.wearable.view.CircularButton;
|
||||
import android.support.wearable.view.GridPagerAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.Command;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.UartConfiguration;
|
||||
|
||||
public class UARTCommandsAdapter extends GridPagerAdapter {
|
||||
private final OnCommandSelectedListener mListener;
|
||||
private UartConfiguration mConfiguration;
|
||||
|
||||
public interface OnCommandSelectedListener {
|
||||
void onCommandSelected(final Command command);
|
||||
}
|
||||
|
||||
public UARTCommandsAdapter(final UartConfiguration configuration, final OnCommandSelectedListener listener) {
|
||||
this.mConfiguration = configuration;
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public void setConfiguration(final UartConfiguration configuration) {
|
||||
// Configuration is null when it has been deleted on the handheld
|
||||
this.mConfiguration = configuration;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount(final int row) {
|
||||
final int count = mConfiguration != null ? mConfiguration.getCommands().length : 0;
|
||||
return count > 0 ? count : 1; // Empty view
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(final ViewGroup viewGroup, final int row, final int column) {
|
||||
final View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.action_item, viewGroup, false);
|
||||
viewGroup.addView(view);
|
||||
|
||||
final Command[] commands = mConfiguration != null ? mConfiguration.getCommands() : null;
|
||||
if (commands != null && commands.length > 0) {
|
||||
final Command command = commands[column];
|
||||
|
||||
final CircularButton icon = view.findViewById(R.id.icon);
|
||||
icon.getImageDrawable().setLevel(command.getIconIndex());
|
||||
icon.setOnClickListener(v -> mListener.onCommandSelected(command));
|
||||
} else {
|
||||
// Hide the icon
|
||||
view.findViewById(R.id.icon).setVisibility(View.GONE);
|
||||
|
||||
// and show the message
|
||||
final TextView emptyView = view.findViewById(R.id.empty);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
if (commands == null)
|
||||
emptyView.setText(R.string.configuration_deleted);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(final ViewGroup viewGroup, final int row, final int column, final Object object) {
|
||||
final View view = (View) object;
|
||||
viewGroup.removeView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(final View view, final Object object) {
|
||||
return view == object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.support.wearable.view.CircledImageView;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
|
||||
public class UARTConfigurationItemLayout extends LinearLayout implements WearableListView.OnCenterProximityListener {
|
||||
private static final int ANIMATION_DURATION_MS = 150;
|
||||
/**
|
||||
* The ratio for the size of a circle in shrink state.
|
||||
*/
|
||||
private static final float SHRINK_CIRCLE_RATIO = .75f;
|
||||
|
||||
private static final float SHRINK_LABEL_ALPHA = .5f;
|
||||
private static final float EXPAND_LABEL_ALPHA = 1f;
|
||||
|
||||
private float mExpandCircleRadius;
|
||||
private float mShrinkCircleRadius;
|
||||
|
||||
private ObjectAnimator mExpandCircleAnimator;
|
||||
private ObjectAnimator mFadeInLabelAnimator;
|
||||
private AnimatorSet mExpandAnimator;
|
||||
|
||||
private ObjectAnimator mShrinkCircleAnimator;
|
||||
private ObjectAnimator mFadeOutLabelAnimator;
|
||||
private AnimatorSet mShrinkAnimator;
|
||||
|
||||
private TextView mName;
|
||||
private CircledImageView mIcon;
|
||||
|
||||
public UARTConfigurationItemLayout(final Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public UARTConfigurationItemLayout(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public UARTConfigurationItemLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
mName = findViewById(R.id.name);
|
||||
mIcon = findViewById(R.id.icon);
|
||||
mExpandCircleRadius = mIcon.getCircleRadius();
|
||||
mShrinkCircleRadius = mExpandCircleRadius * SHRINK_CIRCLE_RATIO;
|
||||
|
||||
mShrinkCircleAnimator = ObjectAnimator.ofFloat(mIcon, "circleRadius", mExpandCircleRadius, mShrinkCircleRadius);
|
||||
mFadeOutLabelAnimator = ObjectAnimator.ofFloat(mName, "alpha", EXPAND_LABEL_ALPHA, SHRINK_LABEL_ALPHA);
|
||||
mShrinkAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
|
||||
mShrinkAnimator.playTogether(mShrinkCircleAnimator, mFadeOutLabelAnimator);
|
||||
|
||||
mExpandCircleAnimator = ObjectAnimator.ofFloat(mIcon, "circleRadius", mShrinkCircleRadius, mExpandCircleRadius);
|
||||
mFadeInLabelAnimator = ObjectAnimator.ofFloat(mName, "alpha", SHRINK_LABEL_ALPHA, EXPAND_LABEL_ALPHA);
|
||||
mExpandAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
|
||||
mExpandAnimator.playTogether(mExpandCircleAnimator, mFadeInLabelAnimator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCenterPosition(final boolean animate) {
|
||||
if (animate) {
|
||||
mShrinkAnimator.cancel();
|
||||
if (!mExpandAnimator.isRunning()) {
|
||||
mExpandCircleAnimator.setFloatValues(mIcon.getCircleRadius(), mExpandCircleRadius);
|
||||
mFadeInLabelAnimator.setFloatValues(mName.getAlpha(), EXPAND_LABEL_ALPHA);
|
||||
mExpandAnimator.start();
|
||||
}
|
||||
} else {
|
||||
mExpandAnimator.cancel();
|
||||
mIcon.setCircleRadius(mExpandCircleRadius);
|
||||
mName.setAlpha(EXPAND_LABEL_ALPHA);
|
||||
}
|
||||
mIcon.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonCenterPosition(final boolean animate) {
|
||||
if (animate) {
|
||||
mExpandAnimator.cancel();
|
||||
if (!mShrinkAnimator.isRunning()) {
|
||||
mShrinkCircleAnimator.setFloatValues(mIcon.getCircleRadius(), mShrinkCircleRadius);
|
||||
mFadeOutLabelAnimator.setFloatValues(mName.getAlpha(), SHRINK_LABEL_ALPHA);
|
||||
mShrinkAnimator.start();
|
||||
}
|
||||
} else {
|
||||
mShrinkAnimator.cancel();
|
||||
mIcon.setCircleRadius(mShrinkCircleRadius);
|
||||
mName.setAlpha(SHRINK_LABEL_ALPHA);
|
||||
}
|
||||
mIcon.setEnabled(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.wearable.DataApi;
|
||||
import com.google.android.gms.wearable.DataEventBuffer;
|
||||
import com.google.android.gms.wearable.DataItem;
|
||||
import com.google.android.gms.wearable.DataItemBuffer;
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
import com.google.android.gms.wearable.DataMapItem;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfileService;
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.UartConfiguration;
|
||||
|
||||
public class UARTConfigurationsActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
|
||||
DataApi.DataListener, GoogleApiClient.OnConnectionFailedListener, WearableListView.ClickListener, MessageApi.MessageListener {
|
||||
private UARTConfigurationsAdapter mAdapter;
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private BleProfileService.LocalBinder mBinder;
|
||||
|
||||
private BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
|
||||
switch (action) {
|
||||
case BleProfileService.BROADCAST_CONNECTION_STATE: {
|
||||
final int state = intent.getIntExtra(BleProfileService.EXTRA_CONNECTION_STATE, BleProfileService.STATE_DISCONNECTED);
|
||||
if (state == BleProfileService.STATE_DISCONNECTED)
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case BleProfileService.BROADCAST_ERROR: {
|
||||
final String message = intent.getStringExtra(BleProfileService.EXTRA_ERROR_MESSAGE);
|
||||
// final int errorCode = intent.getIntExtra(BleProfileService.EXTRA_ERROR_CODE, 0);
|
||||
Toast.makeText(UARTConfigurationsActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
// TODO error handing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName name, final IBinder service) {
|
||||
mBinder = (BleProfileService.LocalBinder) service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
mBinder = null;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_list);
|
||||
|
||||
// Check if the WEAR device is connected to the UART device itself, or by the phone.
|
||||
// Binding will fail if we are using phone as proxy as the service has not been started before.
|
||||
final Intent service = new Intent(this, BleProfileService.class);
|
||||
bindService(service, mServiceConnection, 0);
|
||||
|
||||
final WearableListView listView = findViewById(R.id.list);
|
||||
listView.setClickListener(this);
|
||||
listView.setAdapter(mAdapter = new UARTConfigurationsAdapter(this));
|
||||
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.build();
|
||||
|
||||
|
||||
// Register the broadcast receiver that will listen for events from the device
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BleProfileService.BROADCAST_CONNECTION_STATE);
|
||||
filter.addAction(BleProfileService.BROADCAST_ERROR);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceBroadcastReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mGoogleApiClient.unregisterConnectionCallbacks(this);
|
||||
mGoogleApiClient.unregisterConnectionFailedListener(this);
|
||||
mGoogleApiClient = null;
|
||||
|
||||
// If we were bound to the service, disconnect and unbind. The service will terminate itself when disconnected.
|
||||
if (mBinder != null) {
|
||||
mBinder.disconnect();
|
||||
}
|
||||
unbindService(mServiceConnection);
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceBroadcastReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mGoogleApiClient.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
|
||||
Wearable.DataApi.removeListener(mGoogleApiClient, this);
|
||||
mGoogleApiClient.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(final Bundle bundle) {
|
||||
Wearable.DataApi.addListener(mGoogleApiClient, this);
|
||||
Wearable.MessageApi.addListener(mGoogleApiClient, this);
|
||||
populateConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(final int cause) {
|
||||
Wearable.DataApi.removeListener(mGoogleApiClient, this);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(final ConnectionResult connectionResult) {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChanged(final DataEventBuffer dataEventBuffer) {
|
||||
populateConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(final MessageEvent messageEvent) {
|
||||
// If the activity is bound to service it means that it has connected directly to the device. We ignore messages from the handheld.
|
||||
if (mBinder != null)
|
||||
return;
|
||||
|
||||
switch (messageEvent.getPath()) {
|
||||
case Constants.UART.DEVICE_LINKLOSS:
|
||||
case Constants.UART.DEVICE_DISCONNECTED: {
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(final WearableListView.ViewHolder viewHolder) {
|
||||
if (viewHolder instanceof UARTConfigurationsAdapter.ConfigurationViewHolder) {
|
||||
final UARTConfigurationsAdapter.ConfigurationViewHolder holder = (UARTConfigurationsAdapter.ConfigurationViewHolder) viewHolder;
|
||||
final UartConfiguration configuration = holder.getConfiguration();
|
||||
|
||||
final Intent intent = new Intent(this, UARTCommandsActivity.class);
|
||||
intent.putExtra(UARTCommandsActivity.CONFIGURATION, configuration);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTopEmptyRegionClick() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method read the UART configurations from the DataApi and populates the adapter with them.
|
||||
*/
|
||||
private void populateConfigurations() {
|
||||
if (mGoogleApiClient.isConnected()) {
|
||||
final PendingResult<DataItemBuffer> results = Wearable.DataApi.getDataItems(mGoogleApiClient, Uri.parse("wear:" + Constants.UART.CONFIGURATIONS), DataApi.FILTER_PREFIX);
|
||||
results.setResultCallback(dataItems -> {
|
||||
final List<UartConfiguration> configurations = new ArrayList<>(dataItems.getCount());
|
||||
for (int i = 0; i < dataItems.getCount(); ++i) {
|
||||
final DataItem item = dataItems.get(i);
|
||||
final long id = ContentUris.parseId(item.getUri());
|
||||
final DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
|
||||
final UartConfiguration configuration = new UartConfiguration(dataMap, id);
|
||||
configurations.add(configuration);
|
||||
}
|
||||
mAdapter.setConfigurations(configurations);
|
||||
dataItems.release();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.wearable.view.WearableListView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.domain.UartConfiguration;
|
||||
|
||||
public class UARTConfigurationsAdapter extends WearableListView.Adapter {
|
||||
private final LayoutInflater mInflater;
|
||||
private List<UartConfiguration> mConfigurations;
|
||||
|
||||
public UARTConfigurationsAdapter(final Context context) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the adapter with list of configurations.
|
||||
*/
|
||||
public void setConfigurations(final List<UartConfiguration> configurations) {
|
||||
mConfigurations = configurations;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WearableListView.ViewHolder onCreateViewHolder(final ViewGroup viewGroup, final int viewType) {
|
||||
return new ConfigurationViewHolder(mInflater.inflate(R.layout.configuration_item, viewGroup, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final WearableListView.ViewHolder holder, final int position) {
|
||||
final ConfigurationViewHolder viewHolder = (ConfigurationViewHolder) holder;
|
||||
viewHolder.setConfiguration(mConfigurations.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mConfigurations != null ? mConfigurations.size() : 0;
|
||||
}
|
||||
|
||||
public static class ConfigurationViewHolder extends WearableListView.ViewHolder {
|
||||
private UartConfiguration mConfiguration;
|
||||
private TextView mName;
|
||||
|
||||
public ConfigurationViewHolder(final View itemView) {
|
||||
super(itemView);
|
||||
|
||||
mName = itemView.findViewById(R.id.name);
|
||||
}
|
||||
|
||||
private void setConfiguration(final UartConfiguration configuration) {
|
||||
mConfiguration = configuration;
|
||||
mName.setText(configuration.getName());
|
||||
}
|
||||
|
||||
public UartConfiguration getConfiguration() {
|
||||
return mConfiguration;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfile;
|
||||
import no.nordicsemi.android.nrftoolbox.ble.BleProfileApi;
|
||||
|
||||
public class UARTProfile extends BleProfile {
|
||||
/** Broadcast sent when a UART message is received. */
|
||||
public static final String BROADCAST_DATA_RECEIVED = "no.nordicsemi.android.nrftoolbox.uart.BROADCAST_DATA_RECEIVED";
|
||||
/** The message. */
|
||||
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.EXTRA_DATA";
|
||||
|
||||
/** Nordic UART Service UUID */
|
||||
private static final UUID UART_SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** RX characteristic UUID */
|
||||
private static final UUID UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** TX characteristic UUID */
|
||||
private static final UUID UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
/** The maximum packet size is 20 bytes. */
|
||||
private static final int MAX_PACKET_SIZE = 20;
|
||||
|
||||
/**
|
||||
* This method should return true if the profile matches the given device. That means if the device has the required services.
|
||||
* @param gatt the GATT device
|
||||
* @return true if the device is supported by that profile, false otherwise.
|
||||
*/
|
||||
public static boolean matchDevice(final BluetoothGatt gatt) {
|
||||
final BluetoothGattService service = gatt.getService(UART_SERVICE_UUID);
|
||||
return service != null && service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID) != null && service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID) != null;
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic mTXCharacteristic;
|
||||
private BluetoothGattCharacteristic mRXCharacteristic;
|
||||
private byte[] mOutgoingBuffer;
|
||||
private int mBufferOffset;
|
||||
|
||||
@Override
|
||||
protected Deque<BleManager.Request> initGatt(final BluetoothGatt gatt) {
|
||||
final BluetoothGattService service = gatt.getService(UART_SERVICE_UUID);
|
||||
mTXCharacteristic = service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
|
||||
mRXCharacteristic = service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID);
|
||||
|
||||
final int rxProperties = mRXCharacteristic.getProperties();
|
||||
boolean writeRequest = (rxProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0;
|
||||
|
||||
// Set the WRITE REQUEST type when the characteristic supports it. This will allow to send long write (also if the characteristic support it).
|
||||
// In case there is no WRITE REQUEST property, this manager will divide texts longer then 20 bytes into up to 20 bytes chunks.
|
||||
if (writeRequest)
|
||||
mRXCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
|
||||
// We don't want to enable notifications on TX characteristic as we are not showing them here. A watch may be just used to send data. At least now.
|
||||
// final LinkedList<BleProfileApi.Request> requests = new LinkedList<>();
|
||||
// requests.add(BleProfileApi.Request.newEnableNotificationsRequest(mTXCharacteristic));
|
||||
// return requests;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release() {
|
||||
mTXCharacteristic = null;
|
||||
mRXCharacteristic = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// This method will not be called as notifications were not enabled in initGatt(..).
|
||||
// final Intent intent = new Intent(BROADCAST_DATA_RECEIVED);
|
||||
// intent.putExtra(EXTRA_DATA, characteristic.getStringValue(0));
|
||||
// LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
// When the whole buffer has been sent
|
||||
final byte[] buffer = mOutgoingBuffer;
|
||||
if (mBufferOffset == buffer.length) {
|
||||
mOutgoingBuffer = null;
|
||||
} else { // Otherwise...
|
||||
final int length = Math.min(buffer.length - mBufferOffset, MAX_PACKET_SIZE);
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, mBufferOffset, length));
|
||||
mBufferOffset += length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given text to RX characteristic.
|
||||
* @param text the text to be sent
|
||||
*/
|
||||
public void send(final String text) {
|
||||
// Are we connected?
|
||||
if (mRXCharacteristic == null)
|
||||
return;
|
||||
|
||||
// An outgoing buffer may not be null if there is already another packet being sent. We do nothing in this case.
|
||||
if (!TextUtils.isEmpty(text) && mOutgoingBuffer == null) {
|
||||
final byte[] buffer = mOutgoingBuffer = text.getBytes();
|
||||
mBufferOffset = 0;
|
||||
|
||||
// Depending on whether the characteristic has the WRITE REQUEST property or not, we will either send it as it is (hoping the long write is implemented),
|
||||
// or divide it into up to 20 bytes chunks and send them one by one.
|
||||
final boolean writeRequest = (mRXCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0;
|
||||
|
||||
if (!writeRequest) { // no WRITE REQUEST property
|
||||
final int length = Math.min(buffer.length, MAX_PACKET_SIZE);
|
||||
mBufferOffset += length;
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, 0, length));
|
||||
} else { // there is WRITE REQUEST property, let's try Long Write
|
||||
mBufferOffset = buffer.length;
|
||||
getApi().enqueue(BleProfileApi.Request.newWriteRequest(mRXCharacteristic, buffer, 0, buffer.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart.domain;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
|
||||
public class Command implements Parcelable {
|
||||
public enum Icon {
|
||||
LEFT(0),
|
||||
UP(1),
|
||||
RIGHT(2),
|
||||
DOWN(3),
|
||||
SETTINGS(4),
|
||||
REW(5),
|
||||
PLAY(6),
|
||||
PAUSE(7),
|
||||
STOP(8),
|
||||
FWD(9),
|
||||
INFO(10),
|
||||
NUMBER_1(11),
|
||||
NUMBER_2(12),
|
||||
NUMBER_3(13),
|
||||
NUMBER_4(14),
|
||||
NUMBER_5(15),
|
||||
NUMBER_6(16),
|
||||
NUMBER_7(17),
|
||||
NUMBER_8(18),
|
||||
NUMBER_9(19);
|
||||
|
||||
public int index;
|
||||
|
||||
Icon(final int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Eol {
|
||||
LF(0),
|
||||
CR(1),
|
||||
CR_LF(2);
|
||||
|
||||
public final int index;
|
||||
|
||||
Eol(final int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
private Eol eol = Eol.LF;
|
||||
private Icon icon = Icon.LEFT;
|
||||
private String command;
|
||||
|
||||
/* package */ Command(final DataMap dataMap) {
|
||||
icon = Icon.values()[dataMap.getInt(Constants.UART.Configuration.Command.ICON_ID)];
|
||||
command = dataMap.getString(Constants.UART.Configuration.Command.MESSAGE);
|
||||
eol = Eol.values()[dataMap.getInt(Constants.UART.Configuration.Command.EOL)];
|
||||
}
|
||||
|
||||
private Command(final Parcel in) {
|
||||
icon = (Icon) in.readSerializable();
|
||||
command = in.readString();
|
||||
eol = (Eol) in.readSerializable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command.
|
||||
* @param command the command that will be sent to UART device
|
||||
*/
|
||||
/* package */ void setCommand(final String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new line type.
|
||||
* @param eol end of line terminator
|
||||
*/
|
||||
/* package */ void setEol(final int eol) {
|
||||
this.eol = Eol.values()[eol];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon index.
|
||||
* @param index index of the icon.
|
||||
*/
|
||||
/* package */ void setIconIndex(final int index) {
|
||||
this.icon = Icon.values()[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command that will be sent to UART device.
|
||||
* @return the command
|
||||
*/
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new line type.
|
||||
* @return end of line terminator
|
||||
*/
|
||||
public Eol getEol() {
|
||||
return eol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon index.
|
||||
* @return the icon index
|
||||
*/
|
||||
public int getIconIndex() {
|
||||
return icon.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Command> CREATOR = new Parcelable.Creator<Command>() {
|
||||
@Override
|
||||
public Command createFromParcel(final Parcel in) {
|
||||
return new Command(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] newArray(final int size) {
|
||||
return new Command[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, int flags) {
|
||||
dest.writeSerializable(icon);
|
||||
dest.writeString(command);
|
||||
dest.writeSerializable(eol);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.uart.domain;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
|
||||
public class UartConfiguration implements Parcelable {
|
||||
private long id;
|
||||
private String name;
|
||||
private Command[] commands;
|
||||
|
||||
public UartConfiguration(final DataMap dataMap, final long id) {
|
||||
name = dataMap.getString(Constants.UART.Configuration.NAME);
|
||||
|
||||
final ArrayList<DataMap> maps = dataMap.getDataMapArrayList(Constants.UART.Configuration.COMMANDS);
|
||||
commands = new Command[maps.size()];
|
||||
for (int i = 0; i < maps.size(); ++i) {
|
||||
commands[i] = new Command(maps.get(i));
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private UartConfiguration(final Parcel in) {
|
||||
id = in.readLong();
|
||||
name = in.readString();
|
||||
commands = in.createTypedArray(Command.CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration ID.
|
||||
* @return the ID of the configuration in the handheld's database.
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field name
|
||||
*
|
||||
* @return optional name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of commands. There is always 9 of them.
|
||||
* @return the commands array
|
||||
*/
|
||||
public Command[] getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UartConfiguration> CREATOR = new Parcelable.Creator<UartConfiguration>() {
|
||||
@Override
|
||||
public UartConfiguration createFromParcel(final Parcel in) {
|
||||
return new UartConfiguration(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UartConfiguration[] newArray(final int size) {
|
||||
return new UartConfiguration[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, int flags) {
|
||||
dest.writeLong(id);
|
||||
dest.writeString(name);
|
||||
dest.writeTypedArray(commands, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.wearable;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.NodeApi;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
|
||||
public class ActionReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "ActionReceiver";
|
||||
|
||||
public static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.ACTION_DISCONNECT";
|
||||
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.EXTRA_DATA";
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_DISCONNECT: {
|
||||
final String profile = intent.getStringExtra(EXTRA_DATA);
|
||||
sendMessageToHandheld(context, Constants.ACTION_DISCONNECT, profile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given message to the handheld.
|
||||
* @param path message path
|
||||
* @param message the message
|
||||
*/
|
||||
private void sendMessageToHandheld(final @NonNull Context context, final @NonNull String path, final @NonNull String message) {
|
||||
new Thread(() -> {
|
||||
final GoogleApiClient client = new GoogleApiClient.Builder(context)
|
||||
.addApi(Wearable.API)
|
||||
.build();
|
||||
client.blockingConnect();
|
||||
|
||||
final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(client).await();
|
||||
for(Node node : nodes.getNodes()) {
|
||||
final MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(client, node.getId(), path, message.getBytes()).await();
|
||||
if (!result.getStatus().isSuccess()){
|
||||
Log.w(TAG, "Failed to send " + path + " to " + node.getDisplayName());
|
||||
}
|
||||
}
|
||||
client.disconnect();
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Nordic Semiconductor
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package no.nordicsemi.android.nrftoolbox.wearable;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.wearable.common.Constants;
|
||||
import no.nordicsemi.android.nrftoolbox.uart.UARTConfigurationsActivity;
|
||||
|
||||
public class MainWearableListenerService extends com.google.android.gms.wearable.WearableListenerService {
|
||||
public static final String TAG = "UARTWLS";
|
||||
|
||||
|
||||
private static final int UART_SHOW_CONFIGURATIONS = 1;
|
||||
private static final int UART_DISCONNECT = 2;
|
||||
|
||||
private static final int UART_NOTIFICATION_ID = 1;
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(final MessageEvent messageEvent) {
|
||||
final String message = new String(messageEvent.getData());
|
||||
|
||||
switch (messageEvent.getPath()) {
|
||||
case Constants.UART.DEVICE_CONNECTED: {
|
||||
// Disconnect action
|
||||
final Intent disconnectIntent = new Intent(ActionReceiver.ACTION_DISCONNECT);
|
||||
disconnectIntent.putExtra(ActionReceiver.EXTRA_DATA, Constants.UART.PROFILE);
|
||||
final PendingIntent disconnectAction = PendingIntent.getBroadcast(this, UART_DISCONNECT, disconnectIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
// Open action
|
||||
final Intent intent = new Intent(this, UARTConfigurationsActivity.class);
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(this, UART_SHOW_CONFIGURATIONS, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentTitle(getString(R.string.notif_uart_device_connected))
|
||||
.setContentText(message)
|
||||
.addAction(new NotificationCompat.Action(R.drawable.ic_full_bluetooth, getString(R.string.action_disconnect), disconnectAction))
|
||||
.setLocalOnly(true);
|
||||
NotificationManagerCompat.from(this).notify(UART_NOTIFICATION_ID, builder.build());
|
||||
break;
|
||||
}
|
||||
case Constants.UART.DEVICE_LINKLOSS:
|
||||
case Constants.UART.DEVICE_DISCONNECTED: {
|
||||
NotificationManagerCompat.from(this).cancel(UART_NOTIFICATION_ID);
|
||||
}
|
||||
default:
|
||||
super.onMessageReceived(messageEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(final Node peer) {
|
||||
super.onPeerDisconnected(peer);
|
||||
NotificationManagerCompat.from(this).cancel(UART_NOTIFICATION_ID);
|
||||
}
|
||||
}
|
||||
28
wear/src/main/res/color/item_background.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/button_pressed" android:state_pressed="true" android:state_enabled="true"/>
|
||||
<item android:color="@color/button_disabled" android:state_enabled="false"/>
|
||||
<item android:color="@color/button_normal" />
|
||||
</selector>
|
||||
BIN
wear/src/main/res/drawable-hdpi/ic_bluetooth.png
Normal file
|
After Width: | Height: | Size: 333 B |
BIN
wear/src/main/res/drawable-hdpi/ic_full_bluetooth.png
Normal file
|
After Width: | Height: | Size: 597 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_bluetooth.png
Normal file
|
After Width: | Height: | Size: 340 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_configurations.png
Normal file
|
After Width: | Height: | Size: 93 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_full_bluetooth.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_1.png
Normal file
|
After Width: | Height: | Size: 520 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_2.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_3.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_4.png
Normal file
|
After Width: | Height: | Size: 789 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_5.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_6.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_7.png
Normal file
|
After Width: | Height: | Size: 914 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_8.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_9.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_about.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_down.png
Normal file
|
After Width: | Height: | Size: 586 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_forward.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_left.png
Normal file
|
After Width: | Height: | Size: 564 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_pause.png
Normal file
|
After Width: | Height: | Size: 434 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_play.png
Normal file
|
After Width: | Height: | Size: 848 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_rewind.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_right.png
Normal file
|
After Width: | Height: | Size: 536 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_settings.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_stop.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
wear/src/main/res/drawable-xhdpi/ic_uart_up.png
Normal file
|
After Width: | Height: | Size: 614 B |
45
wear/src/main/res/drawable/ic_uart_action.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:maxLevel="0" android:drawable="@drawable/ic_uart_left" />
|
||||
<item android:maxLevel="1" android:drawable="@drawable/ic_uart_up" />
|
||||
<item android:maxLevel="2" android:drawable="@drawable/ic_uart_right" />
|
||||
<item android:maxLevel="3" android:drawable="@drawable/ic_uart_down" />
|
||||
<item android:maxLevel="4" android:drawable="@drawable/ic_uart_settings" />
|
||||
<item android:maxLevel="5" android:drawable="@drawable/ic_uart_rewind" />
|
||||
<item android:maxLevel="6" android:drawable="@drawable/ic_uart_play" />
|
||||
<item android:maxLevel="7" android:drawable="@drawable/ic_uart_pause" />
|
||||
<item android:maxLevel="8" android:drawable="@drawable/ic_uart_stop" />
|
||||
<item android:maxLevel="9" android:drawable="@drawable/ic_uart_forward" />
|
||||
<item android:maxLevel="10" android:drawable="@drawable/ic_uart_about" />
|
||||
<item android:maxLevel="11" android:drawable="@drawable/ic_uart_1" />
|
||||
<item android:maxLevel="12" android:drawable="@drawable/ic_uart_2" />
|
||||
<item android:maxLevel="13" android:drawable="@drawable/ic_uart_3" />
|
||||
<item android:maxLevel="14" android:drawable="@drawable/ic_uart_4" />
|
||||
<item android:maxLevel="15" android:drawable="@drawable/ic_uart_5" />
|
||||
<item android:maxLevel="16" android:drawable="@drawable/ic_uart_6" />
|
||||
<item android:maxLevel="17" android:drawable="@drawable/ic_uart_7" />
|
||||
<item android:maxLevel="18" android:drawable="@drawable/ic_uart_8" />
|
||||
<item android:maxLevel="19" android:drawable="@drawable/ic_uart_9" />
|
||||
</level-list>
|
||||
47
wear/src/main/res/layout/action_item.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.wearable.view.CircularButton
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="104dp"
|
||||
android:layout_height="104dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_uart_action"
|
||||
android:color="@color/button_normal"
|
||||
app:buttonRippleColor="@color/button_pressed"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/configuration_empty"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
||||
41
wear/src/main/res/layout/activity_grid_pager.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundColor">
|
||||
|
||||
<android.support.wearable.view.GridViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true"/>
|
||||
|
||||
<android.support.wearable.view.DotsPageIndicator
|
||||
android:id="@+id/page_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal|bottom"/>
|
||||
|
||||
</FrameLayout>
|
||||
42
wear/src/main/res/layout/activity_list.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<android.support.wearable.view.BoxInsetLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_box="left|right">
|
||||
|
||||
<android.support.wearable.view.WearableListView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:dividerHeight="0dp"
|
||||
android:scrollbars="none"/>
|
||||
</RelativeLayout>
|
||||
</android.support.wearable.view.BoxInsetLayout>
|
||||
53
wear/src/main/res/layout/activity_list_with_header.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<android.support.wearable.view.BoxInsetLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_box="left|right">
|
||||
|
||||
<android.support.wearable.view.WearableListView
|
||||
android:id="@+id/devices_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:dividerHeight="0dp"
|
||||
android:scrollbars="none"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:gravity="bottom"
|
||||
android:text="@string/devices_list_title"
|
||||
android:textSize="18sp"/>
|
||||
</RelativeLayout>
|
||||
</android.support.wearable.view.BoxInsetLayout>
|
||||
51
wear/src/main/res/layout/configuration_item.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<no.nordicsemi.android.nrftoolbox.uart.UARTConfigurationItemLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<android.support.wearable.view.CircledImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="52dip"
|
||||
android:layout_height="52dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_configurations"
|
||||
app:circle_color="@color/item_background"
|
||||
app:circle_border_width="0dp"
|
||||
app:circle_radius="26dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-condensed-light"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
</no.nordicsemi.android.nrftoolbox.uart.UARTConfigurationItemLayout>
|
||||
62
wear/src/main/res/layout/device_item.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<no.nordicsemi.android.nrftoolbox.DeviceItemLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp">
|
||||
|
||||
<android.support.wearable.view.CircledImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="52dip"
|
||||
android:layout_height="52dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_bluetooth"
|
||||
app:image_tint="@color/actionBarColorDark"
|
||||
app:circle_border_color="@color/actionBarColorDark"
|
||||
app:circle_border_width="2dp"
|
||||
app:circle_radius="26dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_marginTop="10dp"
|
||||
android:fontFamily="sans-serif-condensed-light"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_below="@+id/name"
|
||||
android:fontFamily="sans-serif-condensed-light"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</no.nordicsemi.android.nrftoolbox.DeviceItemLayout>
|
||||
BIN
wear/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
wear/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
wear/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
wear/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
wear/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
wear/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
wear/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
wear/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
wear/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
32
wear/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<color name="actionBarColor">#009CDE</color>
|
||||
<color name="actionBarColorDark">#0081B7</color>
|
||||
<color name="backgroundColor">#003D56</color>
|
||||
|
||||
<color name="button_normal">#2878FF</color>
|
||||
<color name="button_pressed">#2955C5</color>
|
||||
<color name="button_disabled">#BDBDBD</color>
|
||||
</resources>
|
||||
27
wear/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<dimen name="big_circle_radius">22dp</dimen>
|
||||
<dimen name="small_circle_radius">18dp</dimen>
|
||||
</resources>
|
||||
42
wear/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
~ Copyright (c) 2015, Nordic Semiconductor
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
~
|
||||
~ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
~
|
||||
~ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~
|
||||
~ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
|
||||
~ software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
~ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
~ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">nRF Toolbox</string>
|
||||
<string name="not_available">n/a</string>
|
||||
|
||||
<string name="devices_list_title">Bluetooth devices</string>
|
||||
<string name="devices_list_scanning">Scanning for nearby devices…</string>
|
||||
<string name="devices_list_start_scan">Scan for nearby devices</string>
|
||||
<string name="devices_list_available">Available</string>
|
||||
<string name="devices_list_bonded">Bonded</string>
|
||||
<string name="devices_list_bonding">Bonding…</string>
|
||||
<string name="devices_list_device_not_supported">Device not supported.</string>
|
||||
|
||||
<string name="state_connecting">Connecting…</string>
|
||||
|
||||
<string name="configuration_empty">This configuration is empty.</string>
|
||||
<string name="configuration_deleted">Configuration was deleted.</string>
|
||||
|
||||
<string name="action_disconnect">Disconnect</string>
|
||||
<string name="notif_uart_device_connected">UART device connected</string>
|
||||
</resources>
|
||||