mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2026-01-22 16:14:23 +01:00
Support for mutliple profile with connections added
This commit is contained in:
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* 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.profile.multiconnect;
|
||||
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
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 android.support.annotation.StringRes;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import no.nordicsemi.android.log.ILogSession;
|
||||
import no.nordicsemi.android.log.LogContract;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManager;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.ILogger;
|
||||
|
||||
public abstract class BleMulticonnectProfileService extends Service implements BleManagerCallbacks {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "BleMultiProfileService";
|
||||
|
||||
public static final String BROADCAST_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_CONNECTION_STATE";
|
||||
public static final String BROADCAST_SERVICES_DISCOVERED = "no.nordicsemi.android.nrftoolbox.BROADCAST_SERVICES_DISCOVERED";
|
||||
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";
|
||||
|
||||
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_SERVICE_PRIMARY = "no.nordicsemi.android.nrftoolbox.EXTRA_SERVICE_PRIMARY";
|
||||
public static final String EXTRA_SERVICE_SECONDARY = "no.nordicsemi.android.nrftoolbox.EXTRA_SERVICE_SECONDARY";
|
||||
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
|
||||
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 HashMap<BluetoothDevice, BleManager<BleManagerCallbacks>> mBleManagers;
|
||||
private List<BluetoothDevice> mManagedDevices;
|
||||
private Handler mHandler;
|
||||
|
||||
protected boolean mBinded;
|
||||
private boolean mActivityIsChangingConfiguration;
|
||||
|
||||
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);
|
||||
final ILogger logger = getBinder();
|
||||
|
||||
final String stateString = "[Broadcast] Action received: " + BluetoothAdapter.ACTION_STATE_CHANGED + ", state changed to " + state2String(state);
|
||||
logger.log(LogContract.Log.Level.DEBUG, stateString);
|
||||
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
onBluetoothEnabled();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
onBluetoothDisabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String state2String(final int state) {
|
||||
switch (state) {
|
||||
case BluetoothAdapter.STATE_TURNING_ON:
|
||||
return "TURNING ON";
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
return "ON";
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
return "TURNING OFF";
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
return "OFF";
|
||||
default:
|
||||
return "UNKNOWN (" + state + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public class LocalBinder extends Binder implements ILogger, IDeviceLogger {
|
||||
/**
|
||||
* Returns an unmodifiable list of devices managed by the service.
|
||||
* The returned devices do not need to be connected at tha moment. Each of them was however created
|
||||
* using {@link #connect(BluetoothDevice)} method so they might have been connected before and disconnected.
|
||||
* @return unmodifiable list of devices managed by the service
|
||||
*/
|
||||
public final List<BluetoothDevice> getManagedDevices() {
|
||||
return Collections.unmodifiableList(mManagedDevices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the given device. If the device is already connected this method does nothing.
|
||||
* @param device target Bluetooth device
|
||||
*/
|
||||
public void connect(final BluetoothDevice device) {
|
||||
connect(device, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given device to managed and stars connecting to it. If the device is already connected this method does nothing.
|
||||
* @param device target Bluetooth device
|
||||
* @param session log session that has to be used by the device
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void connect(final BluetoothDevice device, final ILogSession session) {
|
||||
BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
if (manager != null) {
|
||||
if (!manager.isConnected())
|
||||
manager.connect(device);
|
||||
// else do nothing, the device is already connected
|
||||
} else {
|
||||
mManagedDevices.add(device);
|
||||
mBleManagers.put(device, manager = initializeManager());
|
||||
manager.setGattCallbacks(BleMulticonnectProfileService.this);
|
||||
manager.setLogger(session);
|
||||
manager.connect(device);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the given device and removes the associated BleManager object.
|
||||
* If the list of BleManagers is empty while the last activity unbinds from the service,
|
||||
* the service will stop itself.
|
||||
* @param device target device to disconnect and forget
|
||||
*/
|
||||
public void disconnect(final BluetoothDevice device) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
if (manager != null && manager.isConnected()) {
|
||||
manager.disconnect();
|
||||
}
|
||||
mManagedDevices.remove(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected to the sensor.
|
||||
* @param device the target device
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
public final boolean isConnected(final BluetoothDevice device) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
return manager != null && manager.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection state of given device.
|
||||
* @param device the target device
|
||||
* @return the connection state, as in {@link BleManager#getConnectionState()}.
|
||||
*/
|
||||
public final int getConnectionState(final BluetoothDevice device) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
return manager != null ? manager.getConnectionState() : BluetoothGatt.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last received battery level value.
|
||||
* @param device the device of which battery level should be returned
|
||||
* @return battery value or -1 if no value was received or Battery Level characteristic was not found
|
||||
*/
|
||||
public int getBatteryValue(final BluetoothDevice device) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
return manager.getBatteryValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bound activity if changing configuration or not.
|
||||
* If <code>false</code>, we will turn off battery level notifications in onUnbind(..) method below.
|
||||
* @param changing true if the bound activity is finishing
|
||||
*/
|
||||
public final void setActivityIsChangingConfiguration(final boolean changing) {
|
||||
mActivityIsChangingConfiguration = changing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final BluetoothDevice device, final int level, final String message) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
if (manager != null)
|
||||
manager.log(level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final BluetoothDevice device, final int level, @StringRes final int messageRes, final Object... params) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
if (manager != null)
|
||||
manager.log(level, messageRes, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final int level, final String message) {
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values())
|
||||
manager.log(level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(final int level, @StringRes final int messageRes, final Object... params) {
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values())
|
||||
manager.log(level, messageRes, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
mBinded = true;
|
||||
return getBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onRebind(final Intent intent) {
|
||||
mBinded = true;
|
||||
|
||||
if (!mActivityIsChangingConfiguration) {
|
||||
onRebind();
|
||||
// This method will read the Battery Level value from each connected device, if possible and then try to enable battery notifications (if it has NOTIFY property).
|
||||
// If the Battery Level characteristic has only the NOTIFY property, it will only try to enable notifications.
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
||||
if (manager.isConnected())
|
||||
manager.readBatteryLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has rebound to the service after being recreated.
|
||||
* This method is not called when the activity was killed to be recreated when the phone orientation changed
|
||||
* if prior to being killed called {@link LocalBinder#setActivityIsChangingConfiguration(boolean)} with parameter true.
|
||||
*/
|
||||
protected void onRebind() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onUnbind(final Intent intent) {
|
||||
mBinded = false;
|
||||
|
||||
if (!mActivityIsChangingConfiguration) {
|
||||
if (!mManagedDevices.isEmpty()) {
|
||||
onUnbind();
|
||||
// When we are connected, but the application is not open, we are not really interested in battery level notifications.
|
||||
// But we will still be receiving other values, if enabled.
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
||||
if (manager.isConnected())
|
||||
manager.setBatteryNotifications(false);
|
||||
}
|
||||
} else {
|
||||
// The last activity has disconnected from the service and there are no devices to manage. The service may be stopped.
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
// We want the onRebind method be called if anything else binds to it again
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has unbound from the service before being finished.
|
||||
* This method is not called when the activity is killed to be recreated when the phone orientation changed.
|
||||
*/
|
||||
protected void onUnbind() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
mHandler = new Handler();
|
||||
|
||||
// Initialize the map of BLE managers
|
||||
mBleManagers = new HashMap<>();
|
||||
mManagedDevices = new ArrayList<>();
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Ble Manager responsible for connecting to a single device.
|
||||
* @return a new BleManager object
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected abstract BleManager initializeManager();
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
onServiceStarted();
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been started.
|
||||
*/
|
||||
protected void onServiceStarted() {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(final Intent rootIntent) {
|
||||
super.onTaskRemoved(rootIntent);
|
||||
// This method is called when user removed the app from Recents.
|
||||
// By default, the service will be killed and recreated immediately after that.
|
||||
// However, all managed devices will be lost and devices will be disconnected.
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
onServiceStopped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the service has been stopped.
|
||||
*/
|
||||
protected void onServiceStopped() {
|
||||
// Unregister broadcast receivers
|
||||
unregisterReceiver(mBluetoothStateBroadcastReceiver);
|
||||
|
||||
// The managers map may not be empty if the service was killed by the system
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
||||
// Service is being destroyed, no need to disconnect manually.
|
||||
manager.close();
|
||||
manager.log(LogContract.Log.Level.INFO, "Service destroyed");
|
||||
}
|
||||
mBleManagers.clear();
|
||||
mManagedDevices.clear();
|
||||
mBleManagers = null;
|
||||
mManagedDevices = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when Bluetooth Adapter has been disabled.
|
||||
*/
|
||||
protected void onBluetoothDisabled() {
|
||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
||||
// Devices were disconnected, no need to disconnect manually.
|
||||
manager.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when Bluetooth Adapter has been enabled. It is also called
|
||||
* 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.
|
||||
* Make sure you call <code>super.onBluetoothEnabled()</code> at this methods reconnects to
|
||||
* devices that were connected before the Bluetooth was turned off.
|
||||
*/
|
||||
protected void onBluetoothEnabled() {
|
||||
for (final BluetoothDevice device : mManagedDevices) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
if (!manager.isConnected())
|
||||
manager.connect(device);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// By default the Battery Level notifications will be enabled only the activity is bound.
|
||||
return mBinded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnecting(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTING);
|
||||
LocalBroadcastManager.getInstance(BleMulticonnectProfileService.this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnected(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnecting(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTING);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnected(final BluetoothDevice device) {
|
||||
mManagedDevices.remove(device);
|
||||
// The BleManager is not removed from the HashMap to keep the device's log session.
|
||||
// mBleManagers.remove(device);
|
||||
|
||||
// Do not use the device argument here unless you change calling onDeviceDisconnected from the binder above
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinklossOccur(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) {
|
||||
final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_SERVICE_PRIMARY, true);
|
||||
broadcast.putExtra(EXTRA_SERVICE_SECONDARY, optionalServicesFound);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
final Intent broadcast = new Intent(BROADCAST_DEVICE_READY);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
// We don't like this device, remove it from both collections
|
||||
mManagedDevices.remove(device);
|
||||
mBleManagers.remove(device);
|
||||
|
||||
final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_SERVICE_PRIMARY, false);
|
||||
broadcast.putExtra(EXTRA_SERVICE_SECONDARY, false);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
|
||||
// no need for disconnecting, it will be disconnected by the manager automatically
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
|
||||
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||
broadcast.putExtra(EXTRA_BATTERY_LEVEL, value);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||
}
|
||||
|
||||
@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, device);
|
||||
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, device);
|
||||
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, device);
|
||||
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.
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
manager.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected void showToast(final int messageResId) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(BleMulticonnectProfileService.this, messageResId, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
* @param message
|
||||
* a message to be shown
|
||||
*/
|
||||
protected void showToast(final String message) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(BleMulticonnectProfileService.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BleManager} object associated with given device, or null if such has not been created.
|
||||
* To create a BleManager call the {@link LocalBinder#connect(BluetoothDevice)} method must be called.
|
||||
* @param device the target device
|
||||
* @return the BleManager or null
|
||||
*/
|
||||
protected BleManager<? extends BleManagerCallbacks> getBleManager(final BluetoothDevice device) {
|
||||
return mBleManagers.get(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unmodifiable list of all managed devices. They don't have to be connected at the moment.
|
||||
* @return list of managed devices
|
||||
*/
|
||||
protected List<BluetoothDevice> getManagedDevices() {
|
||||
return Collections.unmodifiableList(mManagedDevices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of those managed devices that are connected at the moment.
|
||||
* @return list of connected devices
|
||||
*/
|
||||
protected List<BluetoothDevice> getConnectedDevices() {
|
||||
final List<BluetoothDevice> list = new ArrayList<>();
|
||||
for (BluetoothDevice device : mManagedDevices) {
|
||||
if (mBleManagers.get(device).isConnected())
|
||||
list.add(device);
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected to the sensor.
|
||||
* @param device the target device
|
||||
* @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean isConnected(final BluetoothDevice device) {
|
||||
final BleManager<BleManagerCallbacks> manager = mBleManagers.get(device);
|
||||
return manager != null && manager.isConnected();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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.profile.multiconnect;
|
||||
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import no.nordicsemi.android.log.ILogSession;
|
||||
import no.nordicsemi.android.log.LocalLogSession;
|
||||
import no.nordicsemi.android.log.LogContract;
|
||||
import no.nordicsemi.android.log.Logger;
|
||||
import no.nordicsemi.android.nrftoolbox.AppHelpFragment;
|
||||
import no.nordicsemi.android.nrftoolbox.R;
|
||||
import no.nordicsemi.android.nrftoolbox.profile.BleManagerCallbacks;
|
||||
import no.nordicsemi.android.nrftoolbox.scanner.ScannerFragment;
|
||||
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@link BleMulticonnectProfileServiceReadyActivity} activity is designed to be the base class for profile activities that uses services in order to connect
|
||||
* more than one device at the same time. A service extending {@link BleMulticonnectProfileService} is created when the activity is created, and the activity binds to it.
|
||||
* The service returns a binder that may be used to connect, disconnect or manage devices, and notifies the
|
||||
* activity using Local Broadcasts ({@link LocalBroadcastManager}). See {@link BleMulticonnectProfileService} for messages. If the device is not in range it will listen for
|
||||
* it and connect when it become visible. The service exists until all managed devices have been disconnected and unmanaged and the last activity unbinds from it.
|
||||
* </p>
|
||||
* <p>
|
||||
* When user closes the activity (e.g. by pressing Back button) while being connected, the Service remains working. It's remains connected to the devices or still
|
||||
* listens for updates from them. When entering back to the activity, activity will to bind to the service and refresh UI.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class BleMulticonnectProfileServiceReadyActivity<E extends BleMulticonnectProfileService.LocalBinder> extends AppCompatActivity implements
|
||||
ScannerFragment.OnDeviceSelectedListener, BleManagerCallbacks {
|
||||
private static final String TAG = "BleMulticonnectProfileServiceReadyActivity";
|
||||
|
||||
protected static final int REQUEST_ENABLE_BT = 2;
|
||||
|
||||
private E mService;
|
||||
private List<BluetoothDevice> mManagedDevices;
|
||||
|
||||
private final BroadcastReceiver mCommonBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BleMulticonnectProfileService.EXTRA_DEVICE);
|
||||
final String action = intent.getAction();
|
||||
switch (action) {
|
||||
case BleMulticonnectProfileService.BROADCAST_CONNECTION_STATE: {
|
||||
final int state = intent.getIntExtra(BleMulticonnectProfileService.EXTRA_CONNECTION_STATE, BleMulticonnectProfileService.STATE_DISCONNECTED);
|
||||
|
||||
switch (state) {
|
||||
case BleMulticonnectProfileService.STATE_CONNECTED: {
|
||||
onDeviceConnected(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.STATE_DISCONNECTED: {
|
||||
onDeviceDisconnected(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.STATE_LINK_LOSS: {
|
||||
onLinklossOccur(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.STATE_CONNECTING: {
|
||||
onDeviceConnecting(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.STATE_DISCONNECTING: {
|
||||
onDeviceDisconnecting(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// there should be no other actions
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.BROADCAST_SERVICES_DISCOVERED: {
|
||||
final boolean primaryService = intent.getBooleanExtra(BleMulticonnectProfileService.EXTRA_SERVICE_PRIMARY, false);
|
||||
final boolean secondaryService = intent.getBooleanExtra(BleMulticonnectProfileService.EXTRA_SERVICE_SECONDARY, false);
|
||||
|
||||
if (primaryService) {
|
||||
onServicesDiscovered(bluetoothDevice, secondaryService);
|
||||
} else {
|
||||
onDeviceNotSupported(bluetoothDevice);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.BROADCAST_DEVICE_READY: {
|
||||
onDeviceReady(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.BROADCAST_BOND_STATE: {
|
||||
final int state = intent.getIntExtra(BleMulticonnectProfileService.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
||||
switch (state) {
|
||||
case BluetoothDevice.BOND_BONDING:
|
||||
onBondingRequired(bluetoothDevice);
|
||||
break;
|
||||
case BluetoothDevice.BOND_BONDED:
|
||||
onBonded(bluetoothDevice);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.BROADCAST_BATTERY_LEVEL: {
|
||||
final int value = intent.getIntExtra(BleMulticonnectProfileService.EXTRA_BATTERY_LEVEL, -1);
|
||||
if (value > 0)
|
||||
onBatteryValueReceived(bluetoothDevice, value);
|
||||
break;
|
||||
}
|
||||
case BleMulticonnectProfileService.BROADCAST_ERROR: {
|
||||
final String message = intent.getStringExtra(BleMulticonnectProfileService.EXTRA_ERROR_MESSAGE);
|
||||
final int errorCode = intent.getIntExtra(BleMulticonnectProfileService.EXTRA_ERROR_CODE, 0);
|
||||
onError(bluetoothDevice, message, errorCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName name, final IBinder service) {
|
||||
final E bleService = mService = (E) service;
|
||||
bleService.log(LogContract.Log.Level.DEBUG, "Activity bound to the service");
|
||||
mManagedDevices.addAll(bleService.getManagedDevices());
|
||||
onServiceBinded(bleService);
|
||||
|
||||
// and notify user if device is connected
|
||||
for (final BluetoothDevice device : mManagedDevices) {
|
||||
if (bleService.isConnected(device))
|
||||
onDeviceConnected(device);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
mService = null;
|
||||
onServiceUnbinded();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected final void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mManagedDevices = new ArrayList<>();
|
||||
|
||||
ensureBLESupported();
|
||||
if (!isBLEEnabled()) {
|
||||
showBLEDialog();
|
||||
}
|
||||
|
||||
// In onInitialize method a final class may register local broadcast receivers that will listen for events from the service
|
||||
onInitialize(savedInstanceState);
|
||||
// The onCreateView class should... create the view
|
||||
onCreateView(savedInstanceState);
|
||||
|
||||
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Common nRF Toolbox view references are obtained here
|
||||
setUpView();
|
||||
// View is ready to be used
|
||||
onViewCreated(savedInstanceState);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mCommonBroadcastReceiver, makeIntentFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
/*
|
||||
* In comparison to BleProfileServiceReadyActivity this activity always starts the service when started.
|
||||
* Connecting to a device is done by calling mService.connect(BluetoothDevice) method, not startService(...) like there.
|
||||
* The service will stop itself when all devices it manages were disconnected and unmanaged and the last activity unbinds from it.
|
||||
*/
|
||||
final Intent service = new Intent(this, getServiceClass());
|
||||
startService(service);
|
||||
bindService(service, mServiceConnection, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mService != null) {
|
||||
// We don't want to perform some operations (e.g. disable Battery Level notifications) in the service if we are just rotating the screen.
|
||||
// However, when the activity will disappear, we may want to disable some device features to reduce the battery consumption.
|
||||
mService.setActivityIsChangingConfiguration(isChangingConfigurations());
|
||||
// Log it here as there is no callback when the service gets unbound
|
||||
// and the mService will not be available later (the activity doesn't keep log sessions)
|
||||
mService.log(LogContract.Log.Level.DEBUG, "Activity unbound from the service");
|
||||
}
|
||||
|
||||
unbindService(mServiceConnection);
|
||||
mService = null;
|
||||
|
||||
onServiceUnbinded();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mCommonBroadcastReceiver);
|
||||
}
|
||||
|
||||
private static IntentFilter makeIntentFilter() {
|
||||
final IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_CONNECTION_STATE);
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_SERVICES_DISCOVERED);
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_DEVICE_READY);
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_BOND_STATE);
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_BATTERY_LEVEL);
|
||||
intentFilter.addAction(BleMulticonnectProfileService.BROADCAST_ERROR);
|
||||
return intentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when activity binds to the service. The parameter is the object returned in {@link Service#onBind(Intent)} method in your service.
|
||||
* It is safe to obtain managed devices now.
|
||||
*/
|
||||
protected abstract void onServiceBinded(E binder);
|
||||
|
||||
/**
|
||||
* Called when activity unbinds from the service. You may no longer use this binder methods.
|
||||
*/
|
||||
protected abstract void onServiceUnbinded();
|
||||
|
||||
/**
|
||||
* Returns the service class for sensor communication. The service class must derive from {@link BleMulticonnectProfileService} in order to operate with this class.
|
||||
*
|
||||
* @return the service class
|
||||
*/
|
||||
protected abstract Class<? extends BleMulticonnectProfileService> getServiceClass();
|
||||
|
||||
/**
|
||||
* Returns the service interface that may be used to communicate with the sensor. This will return <code>null</code> if the device is disconnected from the
|
||||
* sensor.
|
||||
*
|
||||
* @return the service binder or <code>null</code>
|
||||
*/
|
||||
protected E getService() {
|
||||
return mService;
|
||||
}
|
||||
|
||||
/**
|
||||
* You may do some initialization here. This method is called from {@link #onCreate(Bundle)} before the view was created.
|
||||
*/
|
||||
protected void onInitialize(final Bundle savedInstanceState) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from {@link #onCreate(Bundle)}. This method should build the activity UI, f.e. using {@link #setContentView(int)}. Use to obtain references to
|
||||
* views. Connect/Disconnect button, the device name view and battery level view are manager automatically.
|
||||
*
|
||||
* @param savedInstanceState contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*/
|
||||
protected abstract void onCreateView(final Bundle savedInstanceState);
|
||||
|
||||
/**
|
||||
* Called after the view has been created.
|
||||
*
|
||||
* @param savedInstanceState contains the data it most recently supplied in {@link #onSaveInstanceState(Bundle)}. Note: <b>Otherwise it is null</b>.
|
||||
*/
|
||||
protected void onViewCreated(final Bundle savedInstanceState) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the view and the toolbar has been created.
|
||||
*/
|
||||
protected final void setUpView() {
|
||||
// set GUI
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.help, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to handle menu actions other than home and about.
|
||||
*
|
||||
* @param itemId the menu item id
|
||||
* @return <code>true</code> if action has been handled
|
||||
*/
|
||||
protected boolean onOptionsItemSelected(final int itemId) {
|
||||
// Overwrite when using menu other than R.menu.help
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
final int id = item.getItemId();
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
break;
|
||||
case R.id.action_about:
|
||||
final AppHelpFragment fragment = AppHelpFragment.getInstance(getAboutTextId());
|
||||
fragment.show(getSupportFragmentManager(), "help_fragment");
|
||||
break;
|
||||
default:
|
||||
return onOptionsItemSelected(id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user press ADD DEVICE button. See layout files -> onClick attribute.
|
||||
*/
|
||||
public void onAddDeviceClicked(final View view) {
|
||||
if (isBLEEnabled()) {
|
||||
showDeviceScanningDialog(getFilterUUID());
|
||||
} else {
|
||||
showBLEDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title resource id that will be used to create logger session. If 0 is returned (default) logger will not be used.
|
||||
*
|
||||
* @return the title resource id
|
||||
*/
|
||||
protected int getLoggerProfileTitle() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method may return the local log content provider authority if local log sessions are supported.
|
||||
*
|
||||
* @return local log session content provider URI
|
||||
*/
|
||||
protected Uri getLocalAuthorityLogger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceSelected(final BluetoothDevice device, final String name) {
|
||||
final int titleId = getLoggerProfileTitle();
|
||||
ILogSession logSession = null;
|
||||
if (titleId > 0) {
|
||||
logSession = Logger.newSession(getApplicationContext(), getString(titleId), device.getAddress(), name);
|
||||
// If nRF Logger is not installed we may want to use local logger
|
||||
if (logSession == null && getLocalAuthorityLogger() != null) {
|
||||
logSession = LocalLogSession.newSession(getApplicationContext(), getLocalAuthorityLogger(), device.getAddress(), name);
|
||||
}
|
||||
}
|
||||
|
||||
mService.connect(device, logSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogCanceled() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceConnecting(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDisconnecting(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinklossOccur(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(final BluetoothDevice device, final boolean optionalServicesFound) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceReady(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBondingRequired(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBonded(final BluetoothDevice device) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceNotSupported(final BluetoothDevice device) {
|
||||
showToast(R.string.not_supported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean shouldEnableBatteryLevelNotifications(final BluetoothDevice device) {
|
||||
// This method will never be called.
|
||||
// Please see BleMulticonnectProfileService#shouldEnableBatteryLevelNotifications(BluetoothDevice) instead.
|
||||
throw new UnsupportedOperationException("This method should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
||||
// empty default implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final BluetoothDevice device, final String message, final int errorCode) {
|
||||
DebugLogger.e(TAG, "Error occurred: " + message + ", error code: " + errorCode);
|
||||
showToast(message + " (" + errorCode + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
|
||||
*
|
||||
* @param message a message to be shown
|
||||
*/
|
||||
protected void showToast(final String message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(BleMulticonnectProfileServiceReadyActivity.this, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected void showToast(final int messageResId) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(BleMulticonnectProfileServiceReadyActivity.this, messageResId, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string resource id that will be shown in About box
|
||||
*
|
||||
* @return the about resource id
|
||||
*/
|
||||
protected abstract int getAboutTextId();
|
||||
|
||||
/**
|
||||
* The UUID filter is used to filter out available devices that does not have such UUID in their advertisement packet. See also:
|
||||
* {@link #isChangingConfigurations()}.
|
||||
*
|
||||
* @return the required UUID or <code>null</code>
|
||||
*/
|
||||
protected abstract UUID getFilterUUID();
|
||||
|
||||
/**
|
||||
* Returns unmodifiable list of managed devices. Managed device is a device the was selected on ScannerFragment until it's removed from the managed list.
|
||||
* It does not have to be connected at that moment.
|
||||
* @return unmodifiable list of managed devices
|
||||
*/
|
||||
protected List<BluetoothDevice> getManagedDevices() {
|
||||
return Collections.unmodifiableList(mManagedDevices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the device is connected. Services may not have been discovered yet.
|
||||
* @param device the device to check if it's connected
|
||||
*/
|
||||
protected boolean isDeviceConnected(final BluetoothDevice device) {
|
||||
return mService != null && mService.isConnected(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the scanner fragment.
|
||||
*
|
||||
* @param filter the UUID filter used to filter out available devices. The fragment will always show all bonded devices as there is no information about their
|
||||
* services
|
||||
* @see #getFilterUUID()
|
||||
*/
|
||||
private void showDeviceScanningDialog(final UUID filter) {
|
||||
final ScannerFragment dialog = ScannerFragment.getInstance(filter);
|
||||
dialog.show(getSupportFragmentManager(), "scan_fragment");
|
||||
}
|
||||
|
||||
private void ensureBLESupported() {
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Toast.makeText(this, R.string.no_ble, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isBLEEnabled() {
|
||||
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
final BluetoothAdapter adapter = bluetoothManager.getAdapter();
|
||||
return adapter != null && adapter.isEnabled();
|
||||
}
|
||||
|
||||
protected void showBLEDialog() {
|
||||
final Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.profile.multiconnect;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.support.annotation.StringRes;
|
||||
|
||||
public interface IDeviceLogger {
|
||||
/**
|
||||
* Logs the given message with given log level into the device's log session.
|
||||
* @param device the target device
|
||||
* @param level the log level
|
||||
* @param message the message to be logged
|
||||
*/
|
||||
void log(final BluetoothDevice device, final int level, final String message);
|
||||
|
||||
/**
|
||||
* Logs the given message with given log level into the device's log session.
|
||||
* @param device the target device
|
||||
* @param level the log level
|
||||
* @param messageRes string resource id
|
||||
* @param params additional (optional) parameters used to fill the message
|
||||
*/
|
||||
void log(final BluetoothDevice device, final int level, @StringRes final int messageRes, final Object... params);
|
||||
}
|
||||
Reference in New Issue
Block a user