/* PebbleKeyEventProfile.java Copyright (c) 2015 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.deviceplugin.pebble.profile; import android.content.Intent; import android.os.Bundle; import com.getpebble.android.kit.util.PebbleDictionary; import org.deviceconnect.android.deviceplugin.pebble.PebbleDeviceService; import org.deviceconnect.android.deviceplugin.pebble.util.PebbleManager; import org.deviceconnect.android.deviceplugin.pebble.util.PebbleManager.OnReceivedEventListener; import org.deviceconnect.android.deviceplugin.pebble.util.PebbleManager.OnSendCommandListener; 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 org.deviceconnect.profile.KeyEventProfileConstants; import java.util.List; import static org.deviceconnect.android.deviceplugin.pebble.util.PebbleManager.KEY_STATE_DOWN; import static org.deviceconnect.android.deviceplugin.pebble.util.PebbleManager.KEY_STATE_UP; /** * Pebble Key Event Profile. * * @author NTT DOCOMO, INC. */ public class PebbleKeyEventProfile extends KeyEventProfile { /** KeyEvent profile onDown cache. */ Bundle mOnDownCache = null; /** KeyEvent profile onDown cache time. */ long mOnDownCacheTime = 0; /** KeyEvent profile onUp cache. */ Bundle mOnUpCache = null; /** KeyEvent profile onUp cache time. */ long mOnUpCacheTime = 0; /** KeyEvent profile onKeyChange cache. */ Bundle mOnKeyChangeCache = null; /** KeyEvent profile onKeyChange cache time. */ long mOnKeyChangeCacheTime = 0; /** Touch State move. */ public static final String STATE_UP = "up"; /** Touch State cancel. */ public static final String STATE_DOWN = "down"; /** KeyEvent profile cache retention time (mSec). */ static final long CACHE_RETENTION_TIME = 10000; /** * Attribute: {@value} . */ public static final String ATTRIBUTE_ON_KEY_CHANGE = "onKeyChange"; /** * Get KeyEvent cache data. * * @param attr Attribute. * @return KeyEvent cache data. */ private Bundle getKeyEventCache(final String attr) { long lCurrentTime = System.currentTimeMillis(); if (attr.equalsIgnoreCase(KeyEventProfile.ATTRIBUTE_ON_DOWN)) { if (lCurrentTime - mOnDownCacheTime <= CACHE_RETENTION_TIME) { return mOnDownCache; } else { return null; } } else if (attr.equalsIgnoreCase(KeyEventProfile.ATTRIBUTE_ON_UP)) { if (lCurrentTime - mOnUpCacheTime <= CACHE_RETENTION_TIME) { return mOnUpCache; } else { return null; } } else if (attr.equalsIgnoreCase(ATTRIBUTE_ON_KEY_CHANGE)) { if (lCurrentTime - mOnKeyChangeCacheTime <= CACHE_RETENTION_TIME) { return mOnKeyChangeCache; } else { return null; } } else { return null; } } /** * Set KeyEvent data to cache. * * @param attr Attribute. * @param keyeventData Touch data. */ private void setKeyEventCache(final String attr, final Bundle keyeventData) { long lCurrentTime = System.currentTimeMillis(); if (attr.equalsIgnoreCase(KeyEventProfile.ATTRIBUTE_ON_DOWN)) { mOnDownCache = keyeventData; mOnDownCacheTime = lCurrentTime; } else if (attr.equalsIgnoreCase(KeyEventProfile.ATTRIBUTE_ON_UP)) { mOnUpCache = keyeventData; mOnUpCacheTime = lCurrentTime; } else if (attr.equalsIgnoreCase(ATTRIBUTE_ON_KEY_CHANGE)) { mOnKeyChangeCache = keyeventData; mOnKeyChangeCacheTime = lCurrentTime; } } 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) { Bundle keyevent = getKeyEventCache(ATTRIBUTE_ON_KEY_CHANGE); if (keyevent == null) { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, ""); } else { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, keyevent); } setResult(response, DConnectMessage.RESULT_OK); return true; } }; private final DConnectApi mGetOnDownApi = new GetApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { Bundle keyevent = getKeyEventCache(KeyEventProfile.ATTRIBUTE_ON_DOWN); if (keyevent == null) { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, ""); } else { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, keyevent); } setResult(response, DConnectMessage.RESULT_OK); return true; } }; private final DConnectApi mGetOnUpApi = new GetApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_UP; } @Override public boolean onRequest(final Intent request, final Intent response) { Bundle keyevent = getKeyEventCache(KeyEventProfile.ATTRIBUTE_ON_UP); if (keyevent == null) { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, ""); } else { response.putExtra(KeyEventProfile.PARAM_KEYEVENT, keyevent); } setResult(response, DConnectMessage.RESULT_OK); return true; } }; 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) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send registration request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_KEY_CHANGE); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_PUT); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { if (dic == null) { MessageUtils.setUnknownError(response); } else { // Registration event listener. EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } } sendResponse(response); } }); // Since returning the response asynchronously, it returns false. return false; } }; private final DConnectApi mPutOnDownApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send registration request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_DOWN); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_PUT); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { if (dic == null) { MessageUtils.setUnknownError(response); } else { // Registration event listener. EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } } sendResponse(response); } }); // Since returning the response asynchronously, it returns false. return false; } }; private final DConnectApi mPutOnUpApi = new PutApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_UP; } @Override public boolean onRequest(final Intent request, final Intent response) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send registration request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_UP); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_PUT); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { if (dic == null) { MessageUtils.setUnknownError(response); } else { // Registration event listener. EventError error = EventManager.INSTANCE.addEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } } sendResponse(response); } }); // Since returning the response asynchronously, it returns false. return false; } }; 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) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send cancellation request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_KEY_CHANGE); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_DELETE); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { } }); // Remove event listener. EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; private final DConnectApi mDeleteOnDownApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_DOWN; } @Override public boolean onRequest(final Intent request, final Intent response) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send cancellation request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_DOWN); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_DELETE); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { } }); // Remove event listener. EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; private final DConnectApi mDeleteOnUpApi = new DeleteApi() { @Override public String getAttribute() { return ATTRIBUTE_ON_UP; } @Override public boolean onRequest(final Intent request, final Intent response) { PebbleManager mgr = ((PebbleDeviceService) getContext()).getPebbleManager(); // To Pebble, Send cancellation request of key event. PebbleDictionary dic = new PebbleDictionary(); dic.addInt8(PebbleManager.KEY_PROFILE, (byte) PebbleManager.PROFILE_KEY_EVENT); dic.addInt8(PebbleManager.KEY_ATTRIBUTE, (byte) PebbleManager.KEY_EVENT_ATTRIBUTE_ON_UP); dic.addInt8(PebbleManager.KEY_ACTION, (byte) PebbleManager.ACTION_DELETE); mgr.sendCommandToPebble(dic, new OnSendCommandListener() { @Override public void onReceivedData(final PebbleDictionary dic) { } }); // Remove event listener. EventError error = EventManager.INSTANCE.removeEvent(request); if (error == EventError.NONE) { setResult(response, DConnectMessage.RESULT_OK); } else if (error == EventError.INVALID_PARAMETER) { MessageUtils.setInvalidRequestParameterError(response); } else { MessageUtils.setUnknownError(response); } return true; } }; /** * Constructor. * * @param service Pebble device service. */ public PebbleKeyEventProfile(final PebbleDeviceService service) { service.getPebbleManager().addEventListener(PebbleManager.PROFILE_KEY_EVENT, new OnReceivedEventListener() { @Override public void onReceivedEvent(final PebbleDictionary dic) { // Set event data. Bundle keyevent = new Bundle(); Long lKeyId = dic.getInteger(PebbleManager.KEY_PARAM_KEY_EVENT_ID); int nKeyId = Integer.valueOf(lKeyId.toString()); Long lKeyType = dic.getInteger(PebbleManager.KEY_PARAM_KEY_EVENT_KEY_TYPE); Long lKeyState = dic.getInteger(PebbleManager.KEY_PARAM_KEY_EVENT_KEY_STATE); int nKeyType = Integer.valueOf(lKeyType.toString()); setConfig(keyevent, getConfig(nKeyType, nKeyId)); setId(keyevent, nKeyId + getKeyTypeFlagValue(nKeyType)); // Get event list from event listener. List<Event> evts = null; Long lAttribute = dic.getInteger(PebbleManager.KEY_ATTRIBUTE); if (lAttribute == PebbleManager.KEY_EVENT_ATTRIBUTE_ON_UP) { evts = EventManager.INSTANCE.getEventList(service.getServiceId(), PROFILE_NAME, null, ATTRIBUTE_ON_UP); } else if (lAttribute == PebbleManager.KEY_EVENT_ATTRIBUTE_ON_DOWN) { evts = EventManager.INSTANCE.getEventList(service.getServiceId(), PROFILE_NAME, null, ATTRIBUTE_ON_DOWN); } else { return; } for (Event evt : evts) { String attr = evt.getAttribute(); // Notify each to the event listener. Intent intent = EventManager.createEventMessage(evt); intent.putExtra(KeyEventProfile.PARAM_KEYEVENT, keyevent); ((PebbleDeviceService) getContext()).sendEvent(intent, evt.getAccessToken()); setKeyEventCache(attr, keyevent); } evts = EventManager.INSTANCE.getEventList(service.getServiceId(), PROFILE_NAME, null, ATTRIBUTE_ON_KEY_CHANGE); if (lKeyState == KEY_STATE_UP) { keyevent.putString("state", STATE_UP); } else if (lKeyState == KEY_STATE_DOWN) { keyevent.putString("state", STATE_DOWN); } for (Event evt : evts) { String attr = evt.getAttribute(); // Notify each to the event listener. Intent intent = EventManager.createEventMessage(evt); intent.putExtra(KeyEventProfile.PARAM_KEYEVENT, keyevent); ((PebbleDeviceService) getContext()).sendEvent(intent, evt.getAccessToken()); setKeyEventCache(attr, keyevent); } } }); addApi(mGetOnKeyChangeApi); addApi(mGetOnDownApi); addApi(mGetOnUpApi); addApi(mPutOnKeyChangeApi); addApi(mPutOnDownApi); addApi(mPutOnUpApi); addApi(mDeleteOnKeyChangeApi); addApi(mDeleteOnDownApi); addApi(mDeleteOnUpApi); } /** * Get configuration string. * * @param nType Key Type. * @param nCode Key Code. * @return Configure string. */ private String getConfig(final int nType, final int nCode) { switch (nType) { case PebbleManager.KEY_EVENT_KEY_TYPE_MEDIA: switch (nCode) { case PebbleManager.KEY_EVENT_KEY_ID_UP: return "MEDIA_NEXT"; case PebbleManager.KEY_EVENT_KEY_ID_SELECT: return "MEDIA_PLAY"; case PebbleManager.KEY_EVENT_KEY_ID_DOWN: return "MEDIA_PREVIOUS"; case PebbleManager.KEY_EVENT_KEY_ID_BACK: return "MEDIA_BACK"; default: return ""; } case PebbleManager.KEY_EVENT_KEY_TYPE_DPAD_BUTTON: switch (nCode) { case PebbleManager.KEY_EVENT_KEY_ID_UP: return "DPAD_UP"; case PebbleManager.KEY_EVENT_KEY_ID_SELECT: return "DPAD_CENTER"; case PebbleManager.KEY_EVENT_KEY_ID_DOWN: return "DPAD_DOWN"; case PebbleManager.KEY_EVENT_KEY_ID_BACK: return "DPAD_BACK"; default: return ""; } case PebbleManager.KEY_EVENT_KEY_TYPE_USER: switch (nCode) { case PebbleManager.KEY_EVENT_KEY_ID_UP: return "USER_CANCEL"; case PebbleManager.KEY_EVENT_KEY_ID_SELECT: return "USER_SELECT"; case PebbleManager.KEY_EVENT_KEY_ID_DOWN: return "USER_OK"; case PebbleManager.KEY_EVENT_KEY_ID_BACK: return "USER_BACK"; default: return ""; } case PebbleManager.KEY_EVENT_KEY_TYPE_STD_KEY: default: switch (nCode) { case PebbleManager.KEY_EVENT_KEY_ID_UP: return "UP"; case PebbleManager.KEY_EVENT_KEY_ID_SELECT: return "SELECT"; case PebbleManager.KEY_EVENT_KEY_ID_DOWN: return "DOWN"; case PebbleManager.KEY_EVENT_KEY_ID_BACK: return "BACK"; default: return ""; } } } /** * Get key type flag value. * * @param nType Key Type. * @return Key Type Flag Value. */ private int getKeyTypeFlagValue(final int nType) { switch (nType) { case PebbleManager.KEY_EVENT_KEY_TYPE_MEDIA: return KeyEventProfileConstants.KEYTYPE_MEDIA_CTRL; case PebbleManager.KEY_EVENT_KEY_TYPE_DPAD_BUTTON: return KeyEventProfileConstants.KEYTYPE_DPAD_BUTTON; case PebbleManager.KEY_EVENT_KEY_TYPE_USER: return KeyEventProfileConstants.KEYTYPE_USER; case PebbleManager.KEY_EVENT_KEY_TYPE_STD_KEY: default: return KeyEventProfileConstants.KEYTYPE_STD_KEY; } } }