/* * Copyright (C) 2015 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.switchaccess; import android.content.Context; import android.content.SharedPreferences; import android.view.KeyEvent; import com.android.utils.SharedPreferencesUtils; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Trigger accessibility action when key mapped to that action in preferences is pressed. * There can be multiple keys mapped to single action (i.e. left half of keyboard). */ public class KeyboardAction { private final Runnable mAction; private int mEnabledResId = 0; private boolean mEnabledDefault = false; private final int mAssignedKeysResId; private Set<Long> mTriggerKeys = new HashSet<>(); private final Set<Long> mPressedKeys = new HashSet<>(); /** * @param assignedKeysResId - preference resource id where list of keys is stored * @param action - accessibility action to trigger when this keyboard switch is pressed */ public KeyboardAction(int assignedKeysResId, Runnable action) { mAssignedKeysResId = assignedKeysResId; mAction = action; } /** * @param enabledResId - preference resource id where on/off guard is stored * @param enabledDefault - default value for guard */ public void setEnableGuard(int enabledResId, boolean enabledDefault) { mEnabledResId = enabledResId; mEnabledDefault = enabledDefault; } /** * Process keys assigned to trigger mAction. * Action is sent to processor on the first key pressed down. * No other key in the group will trigger until after all keys in that group have been released. * @param event - hardware keyboard event * @param actionProcessor - will take care of executing action at proper time * @return If true then the event was consumed and should not be delivered to applications, * otherwise it will be delivered as usual. */ public boolean onKeyEvent(KeyEvent event, ActionProcessor actionProcessor, KeyboardActionListener keyboardActionListener) { long extendedKeyCode = KeyComboPreference.keyEventToExtendedKeyCode(event); if (!mTriggerKeys.contains(extendedKeyCode)) { return false; } if (event.getAction() == KeyEvent.ACTION_DOWN) { if (mPressedKeys.isEmpty()) { actionProcessor.process(mAction); if (keyboardActionListener != null) { keyboardActionListener.onKeyboardAction(mAssignedKeysResId); } } mPressedKeys.add(extendedKeyCode); } else if (event.getAction() == KeyEvent.ACTION_UP) { mPressedKeys.remove(extendedKeyCode); } // ignore repeated keys for now return true; } /** * Read key mapping from default preferences for context */ public void refreshPreferences(Context context) { if (mEnabledResId != 0) { final SharedPreferences prefs = SharedPreferencesUtils.getSharedPreferences(context); if (!prefs.getBoolean(context.getString(mEnabledResId), mEnabledDefault)) { setTriggerKeys(Collections.<Long>emptySet()); return; } } setTriggerKeys(KeyComboPreference.getKeyCodesForPreference(context, mAssignedKeysResId)); } void setTriggerKeys(Set<Long> triggerKeys) { mTriggerKeys = triggerKeys; mPressedKeys.retainAll(mTriggerKeys); // remove keys not in new trigger set } public interface KeyboardActionListener { public void onKeyboardAction(int preferenceIdForAction); } }