/*
LinkingProximityProfile.java
Copyright (c) 2016 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.linking.beacon.profile;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import org.deviceconnect.android.deviceplugin.linking.BuildConfig;
import org.deviceconnect.android.deviceplugin.linking.LinkingApplication;
import org.deviceconnect.android.deviceplugin.linking.LinkingDevicePluginService;
import org.deviceconnect.android.deviceplugin.linking.beacon.LinkingBeaconManager;
import org.deviceconnect.android.deviceplugin.linking.beacon.data.GattData;
import org.deviceconnect.android.deviceplugin.linking.beacon.data.LinkingBeacon;
import org.deviceconnect.android.deviceplugin.linking.beacon.service.LinkingBeaconService;
import org.deviceconnect.android.deviceplugin.linking.linking.LinkingDeviceManager;
import org.deviceconnect.android.deviceplugin.linking.LinkingDestroy;
import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventError;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.ProximityProfile;
import org.deviceconnect.android.profile.api.DConnectApi;
import org.deviceconnect.android.profile.api.DeleteApi;
import org.deviceconnect.android.profile.api.GetApi;
import org.deviceconnect.android.profile.api.PutApi;
import org.deviceconnect.message.DConnectMessage;
import java.util.List;
public class LinkingProximityProfile extends ProximityProfile implements LinkingDestroy {
private static final String TAG = "LinkingPlugIn";
private static final int TIMEOUT = 30 * 1000;
public LinkingProximityProfile(final DConnectMessageService service) {
LinkingApplication app = (LinkingApplication) service.getApplication();
LinkingBeaconManager mgr = app.getLinkingBeaconManager();
mgr.addOnBeaconProximityEventListener(mListener);
addApi(mGetOnDeviceProximity);
addApi(mPutOnDeviceProximity);
addApi(mDeleteOnDeviceProximity);
}
private final LinkingBeaconManager.OnBeaconProximityEventListener mListener = new LinkingBeaconManager.OnBeaconProximityEventListener() {
@Override
public void onProximity(final LinkingBeacon beacon, final GattData gatt) {
notifyProximityEvent(beacon, gatt);
}
};
private final DConnectApi mGetOnDeviceProximity = new GetApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_DEVICE_PROXIMITY;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
LinkingBeaconManager mgr = getLinkingBeaconManager();
LinkingBeacon beacon = ((LinkingBeaconService) getService()).getLinkingBeacon();
GattData gatt = beacon.getGattData();
if (gatt != null && System.currentTimeMillis() - gatt.getTimeStamp() < TIMEOUT) {
setResult(response, DConnectMessage.RESULT_OK);
setProximity(response, createProximity(gatt));
mgr.startBeaconScanWithTimeout(TIMEOUT);
return true;
}
mgr.addOnBeaconProximityEventListener(new OnBeaconProximityEventListenerImpl(mgr, beacon) {
@Override
public void onCleanup() {
mBeaconManager.removeOnBeaconProximityEventListener(this);
}
@Override
public void onDisableScan(final String message) {
if (mCleanupFlag) {
return;
}
if (BuildConfig.DEBUG) {
Log.i(TAG, "onTemperature: disable scan.");
}
MessageUtils.setIllegalDeviceStateError(response, message);
sendResponse(response);
}
@Override
public void onTimeout() {
if (mCleanupFlag) {
return;
}
if (BuildConfig.DEBUG) {
Log.i(TAG, "onTemperature: timeout");
}
MessageUtils.setTimeoutError(response);
sendResponse(response);
}
@Override
public synchronized void onProximity(LinkingBeacon beacon, GattData gatt) {
if (mCleanupFlag || !beacon.equals(mBeacon)) {
return;
}
if (BuildConfig.DEBUG) {
Log.i(TAG, "onTemperature: beacon=" + beacon.getDisplayName() + " gatt=" + gatt);
}
setResult(response, DConnectMessage.RESULT_OK);
setProximity(response, createProximity(gatt));
sendResponse(response);
cleanup();
}
});
mgr.startBeaconScanWithTimeout(TIMEOUT);
return false;
}
};
private final DConnectApi mPutOnDeviceProximity = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_DEVICE_PROXIMITY;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
EventError error = EventManager.INSTANCE.addEvent(request);
if (error == EventError.NONE) {
getLinkingBeaconManager().startBeaconScan();
setResult(response, DConnectMessage.RESULT_OK);
} else if (error == EventError.INVALID_PARAMETER) {
MessageUtils.setInvalidRequestParameterError(response);
} else {
MessageUtils.setUnknownError(response);
}
return true;
}
};
private final DConnectApi mDeleteOnDeviceProximity = new DeleteApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_DEVICE_PROXIMITY;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
EventError error = EventManager.INSTANCE.removeEvent(request);
if (error == EventError.NONE) {
if (BeaconUtil.isEmptyEvent(getLinkingBeaconManager())) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "Linking Beacon Event is empty.");
}
getLinkingBeaconManager().stopBeaconScan();
}
setResult(response, DConnectMessage.RESULT_OK);
} else if (error == EventError.INVALID_PARAMETER) {
MessageUtils.setInvalidRequestParameterError(response);
} else {
MessageUtils.setUnknownError(response);
}
return true;
}
};
@Override
public void onDestroy() {
if (BuildConfig.DEBUG) {
Log.i(TAG, "LinkingProximityProfile#destroy: " + getService().getId());
}
getLinkingBeaconManager().removeOnBeaconProximityEventListener(mListener);
}
private Bundle createProximity(final LinkingDeviceManager.Range range) {
Bundle proximity = new Bundle();
setRange(proximity, convertProximityRange(range));
return proximity;
}
private Bundle createProximity(final GattData gatt) {
Bundle proximity = createProximity(gatt.getRange());
setValue(proximity, calcDistance(gatt) * 100);
return proximity;
}
private void notifyProximityEvent(final LinkingBeacon beacon, final GattData gatt) {
if (!beacon.equals(getLinkingBeacon())) {
return;
}
String serviceId = beacon.getServiceId();
List<Event> events = EventManager.INSTANCE.getEventList(serviceId,
PROFILE_NAME, null, ATTRIBUTE_ON_DEVICE_PROXIMITY);
if (events != null && events.size() > 0) {
for (Event event : events) {
Intent intent = EventManager.createEventMessage(event);
setProximity(intent, createProximity(gatt));
sendEvent(intent, event.getAccessToken());
}
}
}
private Range convertProximityRange(final LinkingDeviceManager.Range range) {
switch (range) {
case IMMEDIATE:
return Range.IMMEDIATE;
case NEAR:
return Range.NEAR;
case FAR:
return Range.FAR;
case UNKNOWN:
default:
return Range.UNKNOWN;
}
}
private double calcDistance(final GattData gatt) {
return Math.pow(10.0, (gatt.getTxPower() - gatt.getRssi()) / 20.0);
}
private LinkingBeacon getLinkingBeacon() {
return ((LinkingBeaconService) getService()).getLinkingBeacon();
}
private LinkingBeaconManager getLinkingBeaconManager() {
LinkingApplication app = getLinkingApplication();
return app.getLinkingBeaconManager();
}
private LinkingApplication getLinkingApplication() {
LinkingDevicePluginService service = (LinkingDevicePluginService) getContext();
return (LinkingApplication) service.getApplication();
}
private abstract class OnBeaconProximityEventListenerImpl extends TimeoutSchedule implements
LinkingBeaconManager.OnBeaconProximityEventListener, Runnable {
OnBeaconProximityEventListenerImpl(final LinkingBeaconManager mgr, final LinkingBeacon beacon) {
super(mgr, beacon);
}
}
}