LAMP_Connect_v1

This commit is contained in:
Kapil
2019-12-03 15:21:55 +05:30
commit 8bb6805f69
531 changed files with 44089 additions and 0 deletions

2
wear/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
*.iml

57
wear/build.gradle Normal file
View 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
View 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 *;
#}

View 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>

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}

View File

@@ -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
}
}

View File

@@ -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 &lt;23, 517&gt;.
* @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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
});
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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>

View 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>

View 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>