/* LinkingKeyEventProfile.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.linking.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.LinkingDestroy; import org.deviceconnect.android.deviceplugin.linking.LinkingDevicePluginService; import org.deviceconnect.android.deviceplugin.linking.linking.LinkingDevice; import org.deviceconnect.android.deviceplugin.linking.linking.LinkingDeviceManager; import org.deviceconnect.android.deviceplugin.linking.linking.service.LinkingDeviceService; import org.deviceconnect.android.event.Event; import org.deviceconnect.android.event.EventError; import org.deviceconnect.android.event.EventManager; import org.deviceconnect.android.message.MessageUtils; import org.deviceconnect.android.profile.KeyEventProfile; 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 LinkingKeyEventProfile extends KeyEventProfile implements LinkingDestroy { private static final String TAG = "LinkingPlugIn"; /** * Attribute: {@value} . */ public static final String ATTRIBUTE_ON_KEY_CHANGE = "onKeyChange"; /** * Parameter: {@value} . */ public static final String PARAM_STATE = "state"; public LinkingKeyEventProfile() { addApi(mGetOnDown); addApi(mPutOnDown); addApi(mDeleteOnDown); addApi(mGetOnKeyChangeApi); addApi(mPutOnKeyChangeApi); addApi(mDeleteOnKeyChangeApi); } private final LinkingDeviceManager.OnButtonEventListener mListener = new LinkingDeviceManager.OnButtonEventListener() { @Override public void onButtonEvent(final LinkingDevice device, final int keyCode) { notifyKeyEvent(device, keyCode); } }; private final DConnectApi mGetOnDown = new GetApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { final LinkingDevice device = getDevice(response); if (device == null) { return true; } final LinkingDeviceManager deviceManager = getLinkingDeviceManager(); deviceManager.enableListenButtonEvent(device, new OnKeyEventListenerImpl(device) { @Override public void onCleanup() { deviceManager.disableListenButtonEvent(mDevice, this); } @Override public void onTimeout() { if (mCleanupFlag) { return; } MessageUtils.setTimeoutError(response); sendResponse(response); } @Override public void onButtonEvent(final LinkingDevice device, final int keyCode) { if (mCleanupFlag || !mDevice.equals(device)) { return; } setKeyEvent(response, createKeyEvent(keyCode, System.currentTimeMillis())); sendResponse(response); cleanup(); } }); return false; } }; private final DConnectApi mPutOnDown = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { LinkingDevice device = getDevice(response); if (device == null) { return true; } EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { getLinkingDeviceManager().enableListenButtonEvent(device, mListener); setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; private DConnectApi mDeleteOnDown = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { LinkingDevice device = getDevice(response); if (device == null) { return true; } EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { if (isEmptyEventList(device)) { getLinkingDeviceManager().disableListenButtonEvent(device, mListener); } setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; private final DConnectApi mGetOnKeyChangeApi = new GetApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_KEY_CHANGE; } @Override public boolean onRequest(final Intent request, final Intent response) { final LinkingDevice device = getDevice(response); if (device == null) { return true; } final LinkingDeviceManager deviceManager = getLinkingDeviceManager(); deviceManager.enableListenButtonEvent(device, new OnKeyEventListenerImpl(device) { @Override public void onCleanup() { deviceManager.disableListenButtonEvent(mDevice, this); } @Override public void onTimeout() { if (mCleanupFlag) { return; } MessageUtils.setTimeoutError(response); sendResponse(response); } @Override public void onButtonEvent(final LinkingDevice device, final int keyCode) { if (mCleanupFlag || !mDevice.equals(device)) { return; } Bundle keyEvent = createKeyEvent(keyCode, System.currentTimeMillis()); keyEvent.putString(PARAM_STATE, "down"); setKeyEvent(response, keyEvent); sendResponse(response); cleanup(); } }); return false; } }; private final DConnectApi mPutOnKeyChangeApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_KEY_CHANGE; } @Override public boolean onRequest(final Intent request, final Intent response) { LinkingDevice device = getDevice(response); if (device == null) { return true; } EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { getLinkingDeviceManager().enableListenButtonEvent(device, mListener); setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; private final DConnectApi mDeleteOnKeyChangeApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_KEY_CHANGE; } @Override public boolean onRequest(final Intent request, final Intent response) { LinkingDevice device = getDevice(response); if (device == null) { return true; } EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { if (isEmptyEventList(device)) { getLinkingDeviceManager().disableListenButtonEvent(device, mListener); } 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, "LinkingKeyEventProfile#destroy: " + getService().getId()); } getLinkingDeviceManager().disableListenButtonEvent(getDevice(), mListener); } private boolean isEmptyEventList(final LinkingDevice device) { List<Event> keyDownEvents = EventManager.INSTANCE.getEventList( device.getBdAddress(), PROFILE_NAME, null, ATTRIBUTE_ON_DOWN); List<Event> keyChangeEvents = EventManager.INSTANCE.getEventList( device.getBdAddress(), PROFILE_NAME, null, ATTRIBUTE_ON_KEY_CHANGE); return keyDownEvents.isEmpty() && keyChangeEvents.isEmpty(); } private LinkingDevice getDevice() { return ((LinkingDeviceService) getService()).getLinkingDevice(); } private LinkingDevice getDevice(final Intent response) { LinkingDevice device = getDevice(); if (!device.isConnected()) { MessageUtils.setIllegalDeviceStateError(response, "device not connected"); return null; } return device; } private Bundle createKeyEvent(final int keyCode) { Bundle keyEvent = new Bundle(); keyEvent.putString(PARAM_ID, String.valueOf(KeyEventProfile.KEYTYPE_STD_KEY + keyCode)); return keyEvent; } private Bundle createKeyEvent(final int keyCode, final long timeStamp) { Bundle keyEvent = createKeyEvent(keyCode); keyEvent.putString(PARAM_CONFIG, "" + timeStamp); return keyEvent; } private void setKeyEvent(final Intent intent, final Bundle keyEvent) { intent.putExtra(PARAM_KEYEVENT, keyEvent); } private void notifyKeyEvent(final LinkingDevice device, final int keyCode) { if (BuildConfig.DEBUG) { Log.d(TAG, "notifyKeyEvent: " + device.getDisplayName() + "[" + keyCode + "]"); } String serviceId = device.getBdAddress(); List<Event> keyDownEvents = EventManager.INSTANCE.getEventList(serviceId, PROFILE_NAME, null, ATTRIBUTE_ON_DOWN); if (keyDownEvents != null && keyDownEvents.size() > 0) { for (Event event : keyDownEvents) { Intent intent = EventManager.createEventMessage(event); setKeyEvent(intent, createKeyEvent(keyCode)); sendEvent(intent, event.getAccessToken()); } } List<Event> keyChangeEvents = EventManager.INSTANCE.getEventList(serviceId, PROFILE_NAME, null, ATTRIBUTE_ON_KEY_CHANGE); if (keyChangeEvents != null) { for (Event event : keyChangeEvents) { Bundle keyEvent = createKeyEvent(keyCode); keyEvent.putString(PARAM_STATE, "down"); Intent intent = EventManager.createEventMessage(event); setKeyEvent(intent, keyEvent); sendEvent(intent, event.getAccessToken()); } } } private LinkingDeviceManager getLinkingDeviceManager() { LinkingApplication app = getLinkingApplication(); return app.getLinkingDeviceManager(); } private LinkingApplication getLinkingApplication() { LinkingDevicePluginService service = (LinkingDevicePluginService) getContext(); return (LinkingApplication) service.getApplication(); } private abstract class OnKeyEventListenerImpl extends TimeoutSchedule implements LinkingDeviceManager.OnButtonEventListener { OnKeyEventListenerImpl(final LinkingDevice device) { super(device); } } }