All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.nordicsemi.android.ble.WaitForValueChangedRequest Maven / Gradle / Ivy

There is a newer version: 2.9.0-beta02
Show newest version
/*
 * Copyright (c) 2018, 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.ble;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Handler;
import android.util.Log;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.concurrent.CancellationException;

import no.nordicsemi.android.ble.callback.AfterCallback;
import no.nordicsemi.android.ble.callback.BeforeCallback;
import no.nordicsemi.android.ble.callback.DataReceivedCallback;
import no.nordicsemi.android.ble.callback.FailCallback;
import no.nordicsemi.android.ble.callback.InvalidRequestCallback;
import no.nordicsemi.android.ble.callback.ReadProgressCallback;
import no.nordicsemi.android.ble.callback.SuccessCallback;
import no.nordicsemi.android.ble.callback.profile.ProfileReadResponse;
import no.nordicsemi.android.ble.data.Data;
import no.nordicsemi.android.ble.data.DataFilter;
import no.nordicsemi.android.ble.data.DataMerger;
import no.nordicsemi.android.ble.data.DataStream;
import no.nordicsemi.android.ble.data.PacketFilter;
import no.nordicsemi.android.ble.exception.BluetoothDisabledException;
import no.nordicsemi.android.ble.exception.DeviceDisconnectedException;
import no.nordicsemi.android.ble.exception.InvalidDataException;
import no.nordicsemi.android.ble.exception.InvalidRequestException;
import no.nordicsemi.android.ble.exception.RequestFailedException;

@SuppressWarnings({"unused", "WeakerAccess"})
public final class WaitForValueChangedRequest extends AwaitingRequest implements Operation {
	private ReadProgressCallback progressCallback;
	private DataMerger dataMerger;
	private DataStream buffer;
	private DataFilter filter;
	private PacketFilter packetFilter;
	private boolean deviceDisconnected;
	private boolean bluetoothDisabled;
	private int count = 0;
	private boolean complete = false;

	WaitForValueChangedRequest(@NonNull final Type type,
							   @Nullable final BluetoothGattCharacteristic characteristic) {
		super(type, characteristic);
	}

	WaitForValueChangedRequest(@NonNull final Type type,
							   @Nullable final BluetoothGattDescriptor descriptor) {
		super(type, descriptor);
	}

	@NonNull
	@Override
	WaitForValueChangedRequest setRequestHandler(@NonNull final RequestHandler requestHandler) {
		super.setRequestHandler(requestHandler);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest setHandler(@Nullable final Handler handler) {
		super.setHandler(handler);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest timeout(@IntRange(from = 0) final long timeout) {
		super.timeout(timeout);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest done(@NonNull final SuccessCallback callback) {
		super.done(callback);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest fail(@NonNull final FailCallback callback) {
		super.fail(callback);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest invalid(@NonNull final InvalidRequestCallback callback) {
		super.invalid(callback);
		return this;
	}

	@Override
	@NonNull
	public WaitForValueChangedRequest before(@NonNull final BeforeCallback callback) {
		super.before(callback);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest then(@NonNull final AfterCallback callback) {
		super.then(callback);
		return this;
	}

	@NonNull
	@Override
	public WaitForValueChangedRequest with(@NonNull final DataReceivedCallback callback) {
		super.with(callback);
		return this;
	}

	@NonNull
	public WaitForValueChangedRequest trigger(@NonNull final Operation trigger) {
		super.trigger(trigger);
		return this;
	}

	/**
	 * Sets a filter which allows to skip some incoming data.
	 * 

* This filter filters each received packet before they are given to the data merger. * To filter the complete packet after merging, use {@link #filterPacket(PacketFilter)} instead. * * @param filter the data filter. * @return The request. */ @NonNull public WaitForValueChangedRequest filter(@NonNull final DataFilter filter) { this.filter = filter; return this; } /** * Sets a packet filter which allows to ignore the complete packet. *

* This filter differs from the {@link #filter(DataFilter)}, as it checks the complete * packet, after it has been merged. If there is not merger set, this does the same as * the data filter. * * @param filter the packet filter. * @since 2.4.0 * @return The request. */ @NonNull public WaitForValueChangedRequest filterPacket(@NonNull final PacketFilter filter) { this.packetFilter = filter; return this; } /** * Adds a merger that will be used to merge multiple packets into a single Data. * The merger may modify each packet if necessary. * * @return The request. */ @NonNull public WaitForValueChangedRequest merge(@NonNull final DataMerger merger) { this.dataMerger = merger; this.progressCallback = null; return this; } /** * Adds a merger that will be used to merge multiple packets into a single Data. * The merger may modify each packet if necessary. * * @return The request. */ @NonNull public WaitForValueChangedRequest merge(@NonNull final DataMerger merger, @NonNull final ReadProgressCallback callback) { this.dataMerger = merger; this.progressCallback = callback; return this; } /** * Similar to {@link #await(Class)}, but if the response class extends * {@link ProfileReadResponse} and the received response is invalid, an exception is thrown. * This allows to keep all error handling in one place. * * @param response the result object. * @param a response class that extends {@link ProfileReadResponse}. * @return Object with a valid response. * @throws IllegalStateException thrown when you try to call this method from * the main (UI) thread. * @throws InterruptedException thrown when the timeout occurred before the * characteristic value has changed. * @throws CancellationException thrown when the request has been cancelled. * @throws RequestFailedException thrown when the trigger request has failed. * @throws DeviceDisconnectedException thrown when the device disconnected before the * notification or indication was received. * @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @throws InvalidDataException thrown when the received data were not valid (that is when * {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)} * failed to parse the data correctly and called * {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}). */ @SuppressWarnings("ConstantConditions") @NonNull public E awaitValid(@NonNull final E response) throws RequestFailedException, InvalidDataException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException, InterruptedException, CancellationException{ final E result = await(response); if (result != null && !result.isValid()) { throw new InvalidDataException(result); } return result; } /** * Similar to {@link #await(DataReceivedCallback)}, but if the response class extends * {@link ProfileReadResponse} and the received response is invalid, an exception is thrown. * This allows to keep all error handling in one place. * * @param responseClass the result class. This class will be instantiate, therefore it * has to have a default constructor. * @param a response class that extends {@link ProfileReadResponse}. * @return Object with a valid response. * @throws IllegalStateException thrown when you try to call this method from * the main (UI) thread. * @throws InterruptedException thrown when the timeout occurred before the * characteristic value has changed. * @throws CancellationException thrown when the request has been cancelled. * @throws IllegalArgumentException thrown when the response class could not be instantiated. * @throws RequestFailedException thrown when the trigger request has failed. * @throws DeviceDisconnectedException thrown when the device disconnected before the * notification or indication was received. * @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @throws InvalidDataException thrown when the received data were not valid (that is when * {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)} * failed to parse the data correctly and called * {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}). */ @SuppressWarnings("ConstantConditions") @NonNull public E awaitValid(@NonNull final Class responseClass) throws RequestFailedException, InvalidDataException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException, InterruptedException, CancellationException { final E response = await(responseClass); if (response != null && !response.isValid()) { throw new InvalidDataException(response); } return response; } /** * Same as {@link #await(Class, long)}, but if received response is not valid, this method will * thrown an exception. * * @param responseClass the result class. This class will be instantiate, therefore it * has to have a default constructor. * @param timeout optional timeout in milliseconds. * @param a response class that extends {@link ProfileReadResponse}. * @return Object with a valid response. * @throws IllegalStateException thrown when you try to call this method from * the main (UI) thread. * @throws InterruptedException thrown when the timeout occurred before the * characteristic value has changed. * @throws CancellationException thrown when the request has been cancelled. * @throws IllegalArgumentException thrown when the response class could not be instantiated. * @throws RequestFailedException thrown when the trigger request has failed. * @throws DeviceDisconnectedException thrown when the device disconnected before the * notification or indication was received. * @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @throws InvalidDataException thrown when the received data were not valid (that is when * {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)} * failed to parse the data correctly and called * {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}). * @deprecated Use {@link #timeout(long)} and {@link #awaitValid(Class)} instead. */ @NonNull @Deprecated public E awaitValid(@NonNull final Class responseClass, @IntRange(from = 0) final long timeout) throws InterruptedException, InvalidDataException, RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException, CancellationException { return timeout(timeout).awaitValid(responseClass); } /** * Same as {@link #await(Object, long)}, but if received response is not valid, * this method will thrown an exception. * * @param response the result object. * @param timeout optional timeout in milliseconds. * @param a response class that extends {@link ProfileReadResponse}. * @return Object with a valid response. * @throws IllegalStateException thrown when you try to call this method from * the main (UI) thread. * @throws InterruptedException thrown when the timeout occurred before the * characteristic value has changed. * @throws CancellationException thrown when the request has been cancelled. * @throws RequestFailedException thrown when the trigger request has failed. * @throws DeviceDisconnectedException thrown when the device disconnected before the * notification or indication was received. * @throws BluetoothDisabledException thrown when the Bluetooth adapter has been disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @throws InvalidDataException thrown when the received data were not valid (that is when * {@link ProfileReadResponse#onDataReceived(BluetoothDevice, Data)} * failed to parse the data correctly and called * {@link ProfileReadResponse#onInvalidDataReceived(BluetoothDevice, Data)}). * @deprecated Use {@link #timeout(long)} and {@link #awaitValid(E)} instead. */ @NonNull @Deprecated public E awaitValid(@NonNull final E response, @IntRange(from = 0) final long timeout) throws InterruptedException, InvalidDataException, DeviceDisconnectedException, RequestFailedException, BluetoothDisabledException, InvalidRequestException, CancellationException{ return timeout(timeout).awaitValid(response); } boolean matches(final byte[] packet) { return filter == null || filter.filter(packet); } void notifyValueChanged(final BluetoothDevice device, final byte[] value) { // Keep a reference to the value callback, as it may change during execution final DataReceivedCallback valueCallback = this.valueCallback; // With no value callback there is no need for any merging if (valueCallback == null) { if (packetFilter == null || packetFilter.filter(value)) complete = true; return; } if (dataMerger == null && (packetFilter == null || packetFilter.filter(value))) { complete = true; final Data data = new Data(value); handler.post(() -> { try { valueCallback.onDataReceived(device, data); } catch (final Throwable t) { Log.e(TAG, "Exception in Value callback", t); } }); } else { final int c = count; handler.post(() -> { if (progressCallback != null) { try { progressCallback.onPacketReceived(device, value, c); } catch (final Throwable t) { Log.e(TAG, "Exception in Progress callback", t); } } }); if (buffer == null) buffer = new DataStream(); if (dataMerger.merge(buffer, value, count++)) { final byte[] merged = buffer.toByteArray(); if (packetFilter == null || packetFilter.filter(merged)) { complete = true; final Data data = new Data(merged); handler.post(() -> { try { valueCallback.onDataReceived(device, data); } catch (final Throwable t) { Log.e(TAG, "Exception in Value callback", t); } }); } buffer = null; count = 0; } // else // wait for more packets to be merged } } boolean isComplete() { return complete; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy