full hardware key support
This commit is contained in:
parent
ddabff76a4
commit
3626ac24ea
20 changed files with 542 additions and 494 deletions
|
|
@ -142,15 +142,6 @@ To translate Traditional T9 menus and messages in your language, add: `res/value
|
||||||
|
|
||||||
Alternatively, if you don't have Android Studio, you could just use `res/values/strings.xml` as a reference and translate all strings in your file, skipping the ones that have the `translatable="false"` attribute.
|
Alternatively, if you don't have Android Studio, you could just use `res/values/strings.xml` as a reference and translate all strings in your file, skipping the ones that have the `translatable="false"` attribute.
|
||||||
|
|
||||||
## Adding Support for New Hardware Keys (Hotkeys)
|
|
||||||
TT9 allows assigning hotkeys for performing different functions. If your phone has a special key that does not appear on the Hotkey configuration screen, you can easily add support for it.
|
|
||||||
|
|
||||||
- In `preferences/helpers/Hotkeys.java`, find the `generateList()` function.
|
|
||||||
- Add the new key there. The order of adding is used when displaying the dropdown options.
|
|
||||||
- Optionally, you can translate the name of the key in different languages in the `res/values-XX/strings.xml` files.
|
|
||||||
|
|
||||||
_You can find the key codes [in the Android docs](https://developer.android.com/reference/android/view/KeyEvent)._
|
|
||||||
|
|
||||||
## Contribution Process
|
## Contribution Process
|
||||||
|
|
||||||
### Before you start
|
### Before you start
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import io.github.sspanak.tt9.db.words.DictionaryLoader;
|
||||||
import io.github.sspanak.tt9.ime.helpers.TextField;
|
import io.github.sspanak.tt9.ime.helpers.TextField;
|
||||||
import io.github.sspanak.tt9.ime.modes.InputMode;
|
import io.github.sspanak.tt9.ime.modes.InputMode;
|
||||||
import io.github.sspanak.tt9.ime.modes.InputModeKind;
|
import io.github.sspanak.tt9.ime.modes.InputModeKind;
|
||||||
import io.github.sspanak.tt9.preferences.helpers.Hotkeys;
|
|
||||||
import io.github.sspanak.tt9.ui.UI;
|
import io.github.sspanak.tt9.ui.UI;
|
||||||
import io.github.sspanak.tt9.util.Ternary;
|
import io.github.sspanak.tt9.util.Ternary;
|
||||||
|
|
||||||
|
|
@ -16,7 +15,7 @@ public abstract class HotkeyHandler extends CommandHandler {
|
||||||
protected void onInit() {
|
protected void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
if (settings.areHotkeysInitialized()) {
|
if (settings.areHotkeysInitialized()) {
|
||||||
Hotkeys.setDefault(settings);
|
settings.setDefaultKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
package io.github.sspanak.tt9.ime.helpers;
|
package io.github.sspanak.tt9.ime.helpers;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import io.github.sspanak.tt9.R;
|
||||||
|
import io.github.sspanak.tt9.languages.Language;
|
||||||
|
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
import io.github.sspanak.tt9.util.Ternary;
|
import io.github.sspanak.tt9.util.Ternary;
|
||||||
|
import io.github.sspanak.tt9.util.Text;
|
||||||
|
|
||||||
public class Key {
|
public class Key {
|
||||||
private static final HashMap<Integer, Ternary> handledKeys = new HashMap<>();
|
private static final HashMap<Integer, Ternary> handledKeys = new HashMap<>();
|
||||||
|
|
@ -107,4 +115,57 @@ public class Key {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("GestureBackNavigation") // we are not handling anything here, the warning makes no sense
|
||||||
|
public static String codeToName(@NonNull Context context, int keyCode) {
|
||||||
|
return switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_UNKNOWN -> context.getString(R.string.key_none);
|
||||||
|
case KeyEvent.KEYCODE_POUND -> "#";
|
||||||
|
case KeyEvent.KEYCODE_STAR -> "✱";
|
||||||
|
case KeyEvent.KEYCODE_BACK -> context.getString(R.string.key_back);
|
||||||
|
case KeyEvent.KEYCODE_CALL -> context.getString(R.string.key_call);
|
||||||
|
case KeyEvent.KEYCODE_CHANNEL_DOWN -> context.getString(R.string.key_channel_down);
|
||||||
|
case KeyEvent.KEYCODE_CHANNEL_UP -> context.getString(R.string.key_channel_up);
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP -> context.getString(R.string.key_dpad_up);
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN -> context.getString(R.string.key_dpad_down);
|
||||||
|
case KeyEvent.KEYCODE_DPAD_LEFT -> context.getString(R.string.key_dpad_left);
|
||||||
|
case KeyEvent.KEYCODE_DPAD_RIGHT -> context.getString(R.string.key_dpad_right);
|
||||||
|
case KeyEvent.KEYCODE_MENU -> context.getString(R.string.key_menu);
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_ADD -> "Num +";
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_DIVIDE -> "Num /";
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_DOT -> "Num .";
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_MULTIPLY -> "Num *";
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_SUBTRACT -> "Num -";
|
||||||
|
case KeyEvent.KEYCODE_PROG_RED -> context.getString(R.string.key_red);
|
||||||
|
case KeyEvent.KEYCODE_PROG_GREEN -> context.getString(R.string.key_green);
|
||||||
|
case KeyEvent.KEYCODE_PROG_YELLOW -> context.getString(R.string.key_yellow);
|
||||||
|
case KeyEvent.KEYCODE_PROG_BLUE -> context.getString(R.string.key_blue);
|
||||||
|
case KeyEvent.KEYCODE_SOFT_LEFT -> context.getString(R.string.key_soft_left);
|
||||||
|
case KeyEvent.KEYCODE_SOFT_RIGHT -> context.getString(R.string.key_soft_right);
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_MUTE -> context.getString(R.string.key_volume_mute);
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_DOWN -> context.getString(R.string.key_volume_down);
|
||||||
|
case KeyEvent.KEYCODE_VOLUME_UP -> context.getString(R.string.key_volume_up);
|
||||||
|
default -> codeToSystemName(context, keyCode);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String codeToSystemName(@NonNull Context context, int keyCode) {
|
||||||
|
String name = KeyEvent.keyCodeToString(keyCode).replace("KEYCODE_", "");
|
||||||
|
|
||||||
|
if (new Text(name).isNumeric()) {
|
||||||
|
return context.getString(R.string.key_key) + " #" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Language english = LanguageCollection.getByLanguageCode("en");
|
||||||
|
String[] parts = name.split("_");
|
||||||
|
StringBuilder formattedName = new StringBuilder();
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
formattedName.append(new Text(english, parts[i].toLowerCase()).capitalize());
|
||||||
|
if (i < parts.length - 1) {
|
||||||
|
formattedName.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedName.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import io.github.sspanak.tt9.db.DataStore;
|
||||||
import io.github.sspanak.tt9.db.words.LegacyDb;
|
import io.github.sspanak.tt9.db.words.LegacyDb;
|
||||||
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
|
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
|
||||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||||
import io.github.sspanak.tt9.preferences.helpers.Hotkeys;
|
|
||||||
import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
|
import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
|
||||||
import io.github.sspanak.tt9.preferences.screens.UsageStatsScreen;
|
import io.github.sspanak.tt9.preferences.screens.UsageStatsScreen;
|
||||||
import io.github.sspanak.tt9.preferences.screens.appearance.AppearanceScreen;
|
import io.github.sspanak.tt9.preferences.screens.appearance.AppearanceScreen;
|
||||||
|
|
@ -225,7 +224,7 @@ public class PreferencesActivity extends ActivityWithNavigation implements Prefe
|
||||||
|
|
||||||
private void validateFunctionKeys() {
|
private void validateFunctionKeys() {
|
||||||
if (settings.areHotkeysInitialized()) {
|
if (settings.areHotkeysInitialized()) {
|
||||||
Hotkeys.setDefault(settings);
|
settings.setDefaultKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
package io.github.sspanak.tt9.preferences.helpers;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.view.KeyCharacterMap;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.ViewConfiguration;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import io.github.sspanak.tt9.R;
|
|
||||||
import io.github.sspanak.tt9.preferences.screens.hotkeys.SectionKeymap;
|
|
||||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
|
||||||
|
|
||||||
public class Hotkeys {
|
|
||||||
private final Context context;
|
|
||||||
private final Resources resources;
|
|
||||||
private final String holdKeyTranslation;
|
|
||||||
|
|
||||||
private final LinkedHashMap<String, String> HARDWARE_KEYS = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
public Hotkeys(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
resources = context.getResources();
|
|
||||||
holdKeyTranslation = resources.getString(R.string.key_hold_key);
|
|
||||||
|
|
||||||
addNoKey();
|
|
||||||
generateList();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getHardwareKeyName(String key) {
|
|
||||||
return HARDWARE_KEYS.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Set<String> getHardwareKeys() {
|
|
||||||
return HARDWARE_KEYS.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* setDefault
|
|
||||||
* Applies the default hotkey scheme.
|
|
||||||
*
|
|
||||||
* When a standard "Backspace" hardware key is available, "Backspace" hotkey association is not necessary,
|
|
||||||
* so it will be left out blank, to allow the hardware key do its job.
|
|
||||||
* When the on-screen keyboard is on, "Back" is also not associated, because it will cause weird user
|
|
||||||
* experience. Instead the on-screen "Backspace" key can be used.
|
|
||||||
*
|
|
||||||
* Arrow keys for manipulating suggestions are also assigned only if available.
|
|
||||||
*/
|
|
||||||
public static void setDefault(SettingsStore settings) {
|
|
||||||
HashMap<String, Integer> defaultKeys = new HashMap<>();
|
|
||||||
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_ADD_WORD, KeyEvent.KEYCODE_UNKNOWN); // unassigned
|
|
||||||
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_BACKSPACE, KeyEvent.KEYCODE_BACK);
|
|
||||||
if (
|
|
||||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_CLEAR)
|
|
||||||
|| KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DEL)
|
|
||||||
|| settings.isMainLayoutNumpad()
|
|
||||||
) {
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_BACKSPACE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_COMMAND_PALETTE, -KeyEvent.KEYCODE_STAR); // negative means "hold"
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_EDIT_TEXT, KeyEvent.KEYCODE_UNKNOWN);
|
|
||||||
|
|
||||||
defaultKeys.put(
|
|
||||||
SectionKeymap.ITEM_FILTER_CLEAR,
|
|
||||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_DOWN) ? KeyEvent.KEYCODE_DPAD_DOWN : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
defaultKeys.put(
|
|
||||||
SectionKeymap.ITEM_FILTER_SUGGESTIONS,
|
|
||||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_UP) ? KeyEvent.KEYCODE_DPAD_UP : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
defaultKeys.put(
|
|
||||||
SectionKeymap.ITEM_PREVIOUS_SUGGESTION,
|
|
||||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_LEFT) ? KeyEvent.KEYCODE_DPAD_LEFT : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
defaultKeys.put(
|
|
||||||
SectionKeymap.ITEM_NEXT_SUGGESTION,
|
|
||||||
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_RIGHT) ? KeyEvent.KEYCODE_DPAD_RIGHT : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_NEXT_INPUT_MODE, KeyEvent.KEYCODE_POUND);
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_NEXT_LANGUAGE, -KeyEvent.KEYCODE_POUND); // negative means "hold"
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_SELECT_KEYBOARD, KeyEvent.KEYCODE_UNKNOWN);
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_SHIFT, KeyEvent.KEYCODE_STAR);
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_SPACE_KOREAN, KeyEvent.KEYCODE_STAR);
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_SHOW_SETTINGS, KeyEvent.KEYCODE_UNKNOWN);
|
|
||||||
defaultKeys.put(SectionKeymap.ITEM_VOICE_INPUT, KeyEvent.KEYCODE_UNKNOWN);
|
|
||||||
|
|
||||||
settings.setDefaultKeys(defaultKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* addIfDeviceHasKey
|
|
||||||
* Add the key only if Android says the device has such keypad button or a permanent touch key.
|
|
||||||
*/
|
|
||||||
private void addIfDeviceHasKey(int code, String name, boolean allowHold) {
|
|
||||||
if (
|
|
||||||
(code == KeyEvent.KEYCODE_MENU && ViewConfiguration.get(context).hasPermanentMenuKey())
|
|
||||||
|| KeyCharacterMap.deviceHasKey(code)
|
|
||||||
) {
|
|
||||||
add(code, name, allowHold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* addIfDeviceHasKey
|
|
||||||
* Same as addIfDeviceHasKey, but accepts a Resource String as a key name.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
private void addIfDeviceHasKey(int code, int nameResource, boolean allowHold) {
|
|
||||||
addIfDeviceHasKey(code, resources.getString(nameResource), allowHold);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add
|
|
||||||
* These key will be added as a selectable option, regardless if it exists or or not.
|
|
||||||
* No validation will be performed.
|
|
||||||
*/
|
|
||||||
private void add(int code, String name, boolean allowHold) {
|
|
||||||
HARDWARE_KEYS.put(String.valueOf(code), name);
|
|
||||||
|
|
||||||
if (allowHold) {
|
|
||||||
HARDWARE_KEYS.put(String.valueOf(-code), name + " " + holdKeyTranslation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add
|
|
||||||
* Same as add(), but accepts a Resource String as a key name.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
private void add(int code, int nameResource, boolean allowHold) {
|
|
||||||
add(code, resources.getString(nameResource), allowHold);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* addNoKey
|
|
||||||
* This is the "--" option. The key code matches no key on the keypad.
|
|
||||||
*/
|
|
||||||
private void addNoKey() {
|
|
||||||
add(0, R.string.key_none, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generateList
|
|
||||||
* Generates a list of all supported hotkeys for associating functions in the Settings.
|
|
||||||
*
|
|
||||||
* NOTE: Some TT9 functions do not support all keys. Here you just list all possible options.
|
|
||||||
* Actual validation and assigning happens in SectionKeymap.populate().
|
|
||||||
*/
|
|
||||||
private void generateList() {
|
|
||||||
add(KeyEvent.KEYCODE_CALL, R.string.key_call, true);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_BACK, R.string.key_back, false);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F1, "F1", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F2, "F2", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F3, "F3", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F4, "F4", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F5, "F5", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F6, "F6", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F7, "F7", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F8, "F8", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F9, "F9", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F10, "F10", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F11, "F11", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_F12, "F12", false);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_MENU, R.string.key_menu, true);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_SOFT_LEFT, R.string.key_soft_left, true);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_SOFT_RIGHT, R.string.key_soft_right, true);
|
|
||||||
|
|
||||||
add(KeyEvent.KEYCODE_POUND, "#", true);
|
|
||||||
add(KeyEvent.KEYCODE_STAR, "✱", true);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_DPAD_UP, R.string.key_dpad_up, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_DPAD_DOWN, R.string.key_dpad_down, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_DPAD_LEFT, R.string.key_dpad_left, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_DPAD_RIGHT, R.string.key_dpad_right, false);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_NUMPAD_ADD, "Num +", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_NUMPAD_SUBTRACT, "Num -", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_NUMPAD_MULTIPLY, "Num *", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_NUMPAD_DIVIDE, "Num /", false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_NUMPAD_DOT, "Num .", false);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_CHANNEL_DOWN, R.string.key_channel_down, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_CHANNEL_UP, R.string.key_channel_up, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_PROG_RED, R.string.key_red, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_PROG_GREEN, R.string.key_green, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_PROG_YELLOW, R.string.key_yellow, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_PROG_BLUE, R.string.key_blue, false);
|
|
||||||
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_VOLUME_MUTE, R.string.key_volume_mute, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_VOLUME_DOWN, R.string.key_volume_down, false);
|
|
||||||
addIfDeviceHasKey(KeyEvent.KEYCODE_VOLUME_UP, R.string.key_volume_up, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -55,6 +55,8 @@ public class PreferenceDeletableWord extends ScreenPreference {
|
||||||
context.getString(R.string.delete_words_deleted_confirm_deletion_question, word),
|
context.getString(R.string.delete_words_deleted_confirm_deletion_question, word),
|
||||||
context.getString(R.string.delete_words_delete),
|
context.getString(R.string.delete_words_delete),
|
||||||
this::onDeletionConfirmed,
|
this::onDeletionConfirmed,
|
||||||
|
true,
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
||||||
|
|
||||||
import androidx.preference.DropDownPreference;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import io.github.sspanak.tt9.R;
|
import io.github.sspanak.tt9.R;
|
||||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||||
import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
|
import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
|
||||||
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
|
|
||||||
public class HotkeysScreen extends BaseScreenFragment {
|
public class HotkeysScreen extends BaseScreenFragment {
|
||||||
final public static String NAME = "Hotkeys";
|
public static final String NAME = "Hotkeys";
|
||||||
|
@NonNull static HashMap<String, PreferenceHotkey> hotkeys = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public HotkeysScreen() { init(); }
|
public HotkeysScreen() { init(); }
|
||||||
public HotkeysScreen(PreferencesActivity activity) { init(activity); }
|
public HotkeysScreen(PreferencesActivity activity) { init(activity); }
|
||||||
|
|
||||||
|
|
@ -19,29 +23,22 @@ public class HotkeysScreen extends BaseScreenFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
DropDownPreference[] dropDowns = {
|
getHotkeys();
|
||||||
findPreference(SectionKeymap.ITEM_ADD_WORD),
|
(new ItemResetKeys(findPreference(ItemResetKeys.NAME), activity, hotkeys.values())).enableClickHandler();
|
||||||
findPreference(SectionKeymap.ITEM_BACKSPACE),
|
|
||||||
findPreference(SectionKeymap.ITEM_COMMAND_PALETTE),
|
|
||||||
findPreference(SectionKeymap.ITEM_EDIT_TEXT),
|
|
||||||
findPreference(SectionKeymap.ITEM_FILTER_CLEAR),
|
|
||||||
findPreference(SectionKeymap.ITEM_FILTER_SUGGESTIONS),
|
|
||||||
findPreference(SectionKeymap.ITEM_PREVIOUS_SUGGESTION),
|
|
||||||
findPreference(SectionKeymap.ITEM_NEXT_SUGGESTION),
|
|
||||||
findPreference(SectionKeymap.ITEM_NEXT_INPUT_MODE),
|
|
||||||
findPreference(SectionKeymap.ITEM_NEXT_LANGUAGE),
|
|
||||||
findPreference(SectionKeymap.ITEM_SELECT_KEYBOARD),
|
|
||||||
findPreference(SectionKeymap.ITEM_SHIFT),
|
|
||||||
findPreference(SectionKeymap.ITEM_SPACE_KOREAN),
|
|
||||||
findPreference(SectionKeymap.ITEM_SHOW_SETTINGS),
|
|
||||||
findPreference(SectionKeymap.ITEM_VOICE_INPUT),
|
|
||||||
};
|
|
||||||
SectionKeymap section = new SectionKeymap(Arrays.asList(dropDowns), activity);
|
|
||||||
section.populate().activate();
|
|
||||||
|
|
||||||
(new ItemResetKeys(findPreference(ItemResetKeys.NAME), activity, section))
|
|
||||||
.enableClickHandler();
|
|
||||||
|
|
||||||
resetFontSize(false);
|
resetFontSize(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferenceCount() {
|
||||||
|
return -1; // prevent scrolling and item selection using the number keys on this screen
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getHotkeys() {
|
||||||
|
for (String function : SettingsStore.FUNCTIONS) {
|
||||||
|
PreferenceHotkey hotkey = findPreference(function);
|
||||||
|
if (hotkey != null) {
|
||||||
|
hotkeys.put(function, hotkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import io.github.sspanak.tt9.R;
|
import io.github.sspanak.tt9.R;
|
||||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||||
import io.github.sspanak.tt9.preferences.helpers.Hotkeys;
|
|
||||||
import io.github.sspanak.tt9.preferences.items.ItemClickable;
|
import io.github.sspanak.tt9.preferences.items.ItemClickable;
|
||||||
import io.github.sspanak.tt9.ui.UI;
|
import io.github.sspanak.tt9.ui.UI;
|
||||||
|
|
||||||
|
|
@ -13,19 +14,21 @@ class ItemResetKeys extends ItemClickable {
|
||||||
public static final String NAME = "reset_keys";
|
public static final String NAME = "reset_keys";
|
||||||
|
|
||||||
private final PreferencesActivity activity;
|
private final PreferencesActivity activity;
|
||||||
private final SectionKeymap dropdowns;
|
private final Iterable<PreferenceHotkey> hotkeys;
|
||||||
|
|
||||||
|
|
||||||
ItemResetKeys(Preference item, PreferencesActivity activity, SectionKeymap dropdowns) {
|
ItemResetKeys(@Nullable Preference item, @NonNull PreferencesActivity activity, @NonNull Iterable<PreferenceHotkey> hotkeys) {
|
||||||
super(item);
|
super(item);
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.dropdowns = dropdowns;
|
this.hotkeys = hotkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onClick(Preference p) {
|
protected boolean onClick(Preference p) {
|
||||||
Hotkeys.setDefault(activity.getSettings());
|
activity.getSettings().setDefaultKeys();
|
||||||
dropdowns.reloadSettings();
|
for (PreferenceHotkey hotkey : hotkeys) {
|
||||||
|
hotkey.populate();
|
||||||
|
}
|
||||||
UI.toast(activity, R.string.function_reset_keys_done);
|
UI.toast(activity, R.string.function_reset_keys_done);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class PreferenceBackspaceHotkey extends PreferenceHotkey {
|
||||||
|
public PreferenceBackspaceHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
|
||||||
|
public PreferenceBackspaceHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
|
||||||
|
public PreferenceBackspaceHotkey(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); }
|
||||||
|
public PreferenceBackspaceHotkey(@NonNull Context context) { super(context); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onAssign(DialogInterface dialog, int keyCode) {
|
||||||
|
// backspace works both when pressed short and long,
|
||||||
|
// so separate "hold" and "not hold" options for it make no sense
|
||||||
|
return super.onAssign(dialog, Math.abs(keyCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import io.github.sspanak.tt9.R;
|
||||||
|
import io.github.sspanak.tt9.hacks.DeviceInfo;
|
||||||
|
import io.github.sspanak.tt9.ime.helpers.Key;
|
||||||
|
import io.github.sspanak.tt9.preferences.custom.ScreenPreference;
|
||||||
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
|
import io.github.sspanak.tt9.ui.UI;
|
||||||
|
|
||||||
|
public class PreferenceHotkey extends ScreenPreference implements DialogInterface.OnKeyListener{
|
||||||
|
private static final int CANCEL_KEY = 0;
|
||||||
|
private static final int UNASSIGN_KEY = 2;
|
||||||
|
|
||||||
|
private static SettingsStore settings;
|
||||||
|
|
||||||
|
private int lastKeyDownCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
private int lastLongPressCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
|
||||||
|
|
||||||
|
public PreferenceHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); }
|
||||||
|
public PreferenceHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); }
|
||||||
|
public PreferenceHotkey(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); }
|
||||||
|
public PreferenceHotkey(@NonNull Context context) { super(context); init(context); }
|
||||||
|
|
||||||
|
|
||||||
|
private void init(Context context) {
|
||||||
|
if (settings == null) {
|
||||||
|
settings = new SettingsStore(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttached() {
|
||||||
|
super.onAttached();
|
||||||
|
populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override protected int getLargeLayout() {
|
||||||
|
return DeviceInfo.AT_LEAST_ANDROID_12 ? getDefaultLayout() : R.layout.pref_default_large;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void populate() {
|
||||||
|
populate(settings.getFunctionKey(getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void populate(int keyCode) {
|
||||||
|
String holdSuffix = "";
|
||||||
|
if (keyCode < 0) {
|
||||||
|
holdSuffix = " " + getContext().getString(R.string.key_hold_key);
|
||||||
|
keyCode = -keyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSummary(Key.codeToName(getContext(), keyCode) + holdSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onClick() {
|
||||||
|
super.onClick();
|
||||||
|
UI.confirm(
|
||||||
|
getContext(),
|
||||||
|
getKey(),
|
||||||
|
getContext().getString(R.string.function_assign_instructions, getTitle()),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||||
|
onKeyDown(dialog, keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
onKeyUp(dialog, keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent passing the key event to the activity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onKeyDown(DialogInterface dialog, int keyCode) {
|
||||||
|
if (keyCode == lastKeyDownCode && keyCode != lastLongPressCode && onAssign(dialog, -keyCode)) {
|
||||||
|
lastLongPressCode = keyCode;
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKeyDownCode = keyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onKeyUp(DialogInterface dialog, int keyCode) {
|
||||||
|
lastKeyDownCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
lastLongPressCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
|
if (onIgnore(keyCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onCancel(keyCode) || onUnassign(keyCode) || onAssign(dialog, keyCode)) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected boolean onAssign(DialogInterface dialog, int keyCode) {
|
||||||
|
if (Key.isNumber(keyCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onReassign(dialog, keyCode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setFunctionKey(getKey(), keyCode);
|
||||||
|
populate(keyCode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean onReassign(DialogInterface dialog, int keyCode) {
|
||||||
|
String otherFunction = settings.getFunction(keyCode);
|
||||||
|
if (otherFunction == null || otherFunction.equals(getKey())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Shift" and "Korean Space" can be the same key. It is properly handled in HotkeyHandler.
|
||||||
|
if (
|
||||||
|
(getKey().equals(SettingsStore.FUNC_SPACE_KOREAN) && otherFunction.equals(SettingsStore.FUNC_SHIFT))
|
||||||
|
|| (getKey().equals(SettingsStore.FUNC_SHIFT) && otherFunction.equals(SettingsStore.FUNC_SPACE_KOREAN))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dialog.dismiss();
|
||||||
|
|
||||||
|
PreferenceHotkey otherHotkey = HotkeysScreen.hotkeys.get(otherFunction);
|
||||||
|
CharSequence prettyOtherFunction = otherHotkey != null ? otherHotkey.getTitle() : otherFunction;
|
||||||
|
String question = getContext().getString(
|
||||||
|
R.string.function_already_assigned,
|
||||||
|
Key.codeToName(getContext(), keyCode),
|
||||||
|
prettyOtherFunction,
|
||||||
|
getTitle()
|
||||||
|
);
|
||||||
|
|
||||||
|
UI.confirm(
|
||||||
|
getContext(),
|
||||||
|
getKey(),
|
||||||
|
question,
|
||||||
|
getContext().getString(R.string.function_reassign),
|
||||||
|
() -> {
|
||||||
|
settings.setFunctionKey(otherFunction, KeyEvent.KEYCODE_UNKNOWN);
|
||||||
|
onAssign(dialog, keyCode);
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean onUnassign(int keyCode) {
|
||||||
|
if (Key.codeToNumber(settings, keyCode) != UNASSIGN_KEY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setFunctionKey(getKey(), KeyEvent.KEYCODE_UNKNOWN);
|
||||||
|
populate(KeyEvent.KEYCODE_UNKNOWN);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean onCancel(int keyCode) {
|
||||||
|
return
|
||||||
|
(DeviceInfo.noKeyboard(getContext()) && Key.isBack(keyCode))
|
||||||
|
|| Key.codeToNumber(settings, keyCode) == CANCEL_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean onIgnore(int keyCode) {
|
||||||
|
return Key.isOK(keyCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import io.github.sspanak.tt9.ime.voice.VoiceInputOps;
|
||||||
|
|
||||||
|
public class PreferenceVoiceInputHotkey extends PreferenceHotkey {
|
||||||
|
public PreferenceVoiceInputHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreferenceVoiceInputHotkey(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreferenceVoiceInputHotkey(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreferenceVoiceInputHotkey(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void populate() {
|
||||||
|
boolean isAvailable = new VoiceInputOps(getContext(), null, null, null).isAvailable();
|
||||||
|
setVisible(isAvailable);
|
||||||
|
if (isAvailable) {
|
||||||
|
super.populate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
package io.github.sspanak.tt9.preferences.screens.hotkeys;
|
|
||||||
|
|
||||||
import androidx.preference.DropDownPreference;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import io.github.sspanak.tt9.ime.voice.VoiceInputOps;
|
|
||||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
|
||||||
import io.github.sspanak.tt9.preferences.helpers.Hotkeys;
|
|
||||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
|
||||||
import io.github.sspanak.tt9.util.Logger;
|
|
||||||
|
|
||||||
public class SectionKeymap {
|
|
||||||
public static final String ITEM_ADD_WORD = "key_add_word";
|
|
||||||
public static final String ITEM_BACKSPACE = "key_backspace";
|
|
||||||
public static final String ITEM_COMMAND_PALETTE = "key_command_palette";
|
|
||||||
public static final String ITEM_EDIT_TEXT = "key_edit_text";
|
|
||||||
public static final String ITEM_FILTER_CLEAR = "key_filter_clear";
|
|
||||||
public static final String ITEM_FILTER_SUGGESTIONS = "key_filter_suggestions";
|
|
||||||
public static final String ITEM_PREVIOUS_SUGGESTION = "key_previous_suggestion";
|
|
||||||
public static final String ITEM_NEXT_SUGGESTION = "key_next_suggestion";
|
|
||||||
public static final String ITEM_NEXT_INPUT_MODE = "key_next_input_mode";
|
|
||||||
public static final String ITEM_NEXT_LANGUAGE = "key_next_language";
|
|
||||||
public static final String ITEM_SELECT_KEYBOARD = "key_select_keyboard";
|
|
||||||
public static final String ITEM_SHIFT = "key_shift";
|
|
||||||
public static final String ITEM_SPACE_KOREAN = "key_space_korean";
|
|
||||||
public static final String ITEM_SHOW_SETTINGS = "key_show_settings";
|
|
||||||
public static final String ITEM_VOICE_INPUT = "key_voice_input";
|
|
||||||
|
|
||||||
private final Hotkeys hotkeys;
|
|
||||||
private final Collection<DropDownPreference> items;
|
|
||||||
private final SettingsStore settings;
|
|
||||||
private final boolean isVoiceInputAvailable;
|
|
||||||
|
|
||||||
|
|
||||||
public SectionKeymap(Collection<DropDownPreference> dropDowns, PreferencesActivity activity) {
|
|
||||||
items = dropDowns;
|
|
||||||
hotkeys = new Hotkeys(activity);
|
|
||||||
this.settings = activity.getSettings();
|
|
||||||
isVoiceInputAvailable = new VoiceInputOps(activity, null, null, null).isAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void reloadSettings() {
|
|
||||||
for (DropDownPreference dropDown : items) {
|
|
||||||
int keypadKey = settings.getFunctionKey(dropDown.getKey());
|
|
||||||
dropDown.setValue(String.valueOf(keypadKey));
|
|
||||||
}
|
|
||||||
populate();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public SectionKeymap populate() {
|
|
||||||
populateOtherItems(null);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void activate() {
|
|
||||||
for (DropDownPreference item : items) {
|
|
||||||
onItemClick(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void populateOtherItems(DropDownPreference itemToSkip) {
|
|
||||||
for (DropDownPreference item : items) {
|
|
||||||
if (itemToSkip != null && item != null && Objects.equals(itemToSkip.getKey(), item.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
populateItem(item);
|
|
||||||
previewCurrentKey(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void populateItem(DropDownPreference dropDown) {
|
|
||||||
if (dropDown == null) {
|
|
||||||
Logger.w("SectionKeymap.populateItem", "Cannot populate a NULL item. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<String> keys = new ArrayList<>();
|
|
||||||
for (String key : hotkeys.getHardwareKeys()) {
|
|
||||||
if (
|
|
||||||
validateKey(dropDown, String.valueOf(key))
|
|
||||||
// backspace works both when pressed short and long,
|
|
||||||
// so separate "hold" and "not hold" options for it make no sense
|
|
||||||
&& !(dropDown.getKey().equals(ITEM_BACKSPACE) && Integer.parseInt(key) < 0)
|
|
||||||
) {
|
|
||||||
keys.add(String.valueOf(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<String> values = new ArrayList<>();
|
|
||||||
for (String key : keys) {
|
|
||||||
values.add(hotkeys.getHardwareKeyName(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDown.setEntries(values.toArray(new CharSequence[0]));
|
|
||||||
dropDown.setEntryValues(keys.toArray(new CharSequence[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void onItemClick(DropDownPreference item) {
|
|
||||||
if (item == null) {
|
|
||||||
Logger.w("SectionKeymap.populateItem", "Cannot set a click listener a NULL item. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.setOnPreferenceChangeListener((preference, newKey) -> {
|
|
||||||
if (!validateKey((DropDownPreference) preference, newKey.toString())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
((DropDownPreference) preference).setValue(newKey.toString());
|
|
||||||
previewCurrentKey((DropDownPreference) preference, newKey.toString());
|
|
||||||
populateOtherItems((DropDownPreference) preference);
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logger.e("SectionKeymap.onItemClick", "Failed setting new hotkey. " + e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void previewCurrentKey(DropDownPreference dropDown) {
|
|
||||||
previewCurrentKey(dropDown, dropDown != null ? dropDown.getValue() : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void previewCurrentKey(DropDownPreference dropDown, String key) {
|
|
||||||
if (dropDown == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDown.setSummary(hotkeys.getHardwareKeyName(key));
|
|
||||||
|
|
||||||
if (dropDown.getKey().equals(ITEM_VOICE_INPUT)) {
|
|
||||||
dropDown.setVisible(isVoiceInputAvailable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean validateKey(DropDownPreference dropDown, String key) {
|
|
||||||
if (dropDown == null || key == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.equals("0")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DropDownPreference item : items) {
|
|
||||||
if (item == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Shift" and "Korean Space" can be the same key. It is properly handled in HotkeyHandler.
|
|
||||||
if (
|
|
||||||
(
|
|
||||||
(dropDown.getKey().equals(ITEM_SHIFT) && item.getKey().equals(ITEM_SPACE_KOREAN))
|
|
||||||
|| (dropDown.getKey().equals(ITEM_SPACE_KOREAN) && item.getKey().equals(ITEM_SHIFT))
|
|
||||||
)
|
|
||||||
&& key.equals(item.getValue())
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dropDown.getKey().equals(item.getKey()) && key.equals(item.getValue())) {
|
|
||||||
Logger.i("SectionKeymap.validateKey", "Key: '" + key + "' is already in use for function: " + item.getKey());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,6 +6,8 @@ import android.content.SharedPreferences;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
class BaseSettings {
|
class BaseSettings {
|
||||||
|
protected final String LOG_TAG = SettingsInput.class.getSimpleName();
|
||||||
|
|
||||||
protected final Context context;
|
protected final Context context;
|
||||||
protected final SharedPreferences prefs;
|
protected final SharedPreferences prefs;
|
||||||
protected final SharedPreferences.Editor prefsEditor;
|
protected final SharedPreferences.Editor prefsEditor;
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,113 @@
|
||||||
package io.github.sspanak.tt9.preferences.settings;
|
package io.github.sspanak.tt9.preferences.settings;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import io.github.sspanak.tt9.util.Logger;
|
||||||
|
|
||||||
import io.github.sspanak.tt9.preferences.screens.hotkeys.SectionKeymap;
|
public class SettingsHotkeys extends SettingsUI {
|
||||||
|
|
||||||
class SettingsHotkeys extends SettingsHacks {
|
|
||||||
private static final String HOTKEY_VERSION = "hotkeys_v5";
|
private static final String HOTKEY_VERSION = "hotkeys_v5";
|
||||||
|
|
||||||
|
public static final String FUNC_ADD_WORD = "key_add_word";
|
||||||
|
public static final String FUNC_BACKSPACE = "key_backspace";
|
||||||
|
public static final String FUNC_COMMAND_PALETTE = "key_command_palette";
|
||||||
|
public static final String FUNC_EDIT_TEXT = "key_edit_text";
|
||||||
|
public static final String FUNC_FILTER_CLEAR = "key_filter_clear";
|
||||||
|
public static final String FUNC_FILTER_SUGGESTIONS = "key_filter_suggestions";
|
||||||
|
public static final String FUNC_PREVIOUS_SUGGESTION = "key_previous_suggestion";
|
||||||
|
public static final String FUNC_NEXT_SUGGESTION = "key_next_suggestion";
|
||||||
|
public static final String FUNC_NEXT_INPUT_MODE = "key_next_input_mode";
|
||||||
|
public static final String FUNC_NEXT_LANGUAGE = "key_next_language";
|
||||||
|
public static final String FUNC_SELECT_KEYBOARD = "key_select_keyboard";
|
||||||
|
public static final String FUNC_SHIFT = "key_shift";
|
||||||
|
public static final String FUNC_SPACE_KOREAN = "key_space_korean";
|
||||||
|
public static final String FUNC_SHOW_SETTINGS = "key_show_settings";
|
||||||
|
public static final String FUNC_VOICE_INPUT = "key_voice_input";
|
||||||
|
|
||||||
|
public static final String[] FUNCTIONS = {
|
||||||
|
FUNC_ADD_WORD,
|
||||||
|
FUNC_BACKSPACE,
|
||||||
|
FUNC_COMMAND_PALETTE,
|
||||||
|
FUNC_EDIT_TEXT,
|
||||||
|
FUNC_FILTER_CLEAR,
|
||||||
|
FUNC_FILTER_SUGGESTIONS,
|
||||||
|
FUNC_PREVIOUS_SUGGESTION,
|
||||||
|
FUNC_NEXT_SUGGESTION,
|
||||||
|
FUNC_NEXT_INPUT_MODE,
|
||||||
|
FUNC_NEXT_LANGUAGE,
|
||||||
|
FUNC_SELECT_KEYBOARD,
|
||||||
|
FUNC_SHIFT,
|
||||||
|
FUNC_SPACE_KOREAN,
|
||||||
|
FUNC_SHOW_SETTINGS,
|
||||||
|
FUNC_VOICE_INPUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
SettingsHotkeys(Context context) { super(context); }
|
SettingsHotkeys(Context context) { super(context); }
|
||||||
|
|
||||||
|
|
||||||
public boolean areHotkeysInitialized() {
|
public boolean areHotkeysInitialized() {
|
||||||
return !prefs.getBoolean(HOTKEY_VERSION, false);
|
return !prefs.getBoolean(HOTKEY_VERSION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultKeys(HashMap<String, Integer> defaultKeys) {
|
|
||||||
for (String key : defaultKeys.keySet()) {
|
/**
|
||||||
prefsEditor.putString(key, String.valueOf(defaultKeys.get(key)));
|
* Applies the default hotkey scheme.
|
||||||
|
* When a standard "Backspace" hardware key is available, "Backspace" hotkey association is not necessary,
|
||||||
|
* so it will be left out blank, to allow the hardware key do its job.
|
||||||
|
* When the on-screen keyboard is on, "Back" is also not associated, because it will cause weird user
|
||||||
|
* experience. Instead the on-screen "Backspace" key can be used.
|
||||||
|
* Arrow keys for manipulating suggestions are also assigned only if available.
|
||||||
|
*/
|
||||||
|
public void setDefaultKeys() {
|
||||||
|
// no default keys
|
||||||
|
String[] unassigned = { FUNC_ADD_WORD, FUNC_EDIT_TEXT, FUNC_SELECT_KEYBOARD, FUNC_SHOW_SETTINGS, FUNC_VOICE_INPUT };
|
||||||
|
for (String key : unassigned) {
|
||||||
|
prefsEditor.putString(key, String.valueOf(KeyEvent.KEYCODE_UNKNOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// backspace
|
||||||
|
if (
|
||||||
|
KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_CLEAR)
|
||||||
|
|| KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DEL)
|
||||||
|
|| isMainLayoutNumpad()
|
||||||
|
) {
|
||||||
|
prefsEditor.putString(FUNC_BACKSPACE, String.valueOf(KeyEvent.KEYCODE_UNKNOWN));
|
||||||
|
} else {
|
||||||
|
prefsEditor.putString(FUNC_BACKSPACE, String.valueOf(KeyEvent.KEYCODE_BACK));
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter clear
|
||||||
|
prefsEditor.putString(
|
||||||
|
FUNC_FILTER_CLEAR,
|
||||||
|
String.valueOf(KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_DOWN) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_UNKNOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
// filter
|
||||||
|
prefsEditor.putString(
|
||||||
|
FUNC_FILTER_SUGGESTIONS,
|
||||||
|
String.valueOf(KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_UP) ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_UNKNOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
// previous suggestion
|
||||||
|
prefsEditor.putString(
|
||||||
|
FUNC_PREVIOUS_SUGGESTION,
|
||||||
|
String.valueOf(KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_LEFT) ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_UNKNOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
// next suggestion
|
||||||
|
prefsEditor.putString(
|
||||||
|
FUNC_NEXT_SUGGESTION,
|
||||||
|
String.valueOf(KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DPAD_RIGHT) ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_UNKNOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
prefsEditor.putString(FUNC_COMMAND_PALETTE, String.valueOf(-KeyEvent.KEYCODE_STAR)); // negative means "hold"
|
||||||
|
prefsEditor.putString(FUNC_NEXT_INPUT_MODE, String.valueOf(KeyEvent.KEYCODE_POUND));
|
||||||
|
prefsEditor.putString(FUNC_NEXT_LANGUAGE, String.valueOf(-KeyEvent.KEYCODE_POUND)); // negative means "hold"
|
||||||
|
prefsEditor.putString(FUNC_SHIFT, String.valueOf(KeyEvent.KEYCODE_STAR));
|
||||||
|
prefsEditor.putString(FUNC_SPACE_KOREAN, String.valueOf(KeyEvent.KEYCODE_STAR));
|
||||||
|
|
||||||
prefsEditor.putBoolean(HOTKEY_VERSION, true).apply();
|
prefsEditor.putBoolean(HOTKEY_VERSION, true).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,49 +117,84 @@ class SettingsHotkeys extends SettingsHacks {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setFunctionKey(String functionName, int keyCode) {
|
||||||
|
if (isValidFunction(functionName)) {
|
||||||
|
Logger.d(LOG_TAG, "Setting hotkey for function: '" + functionName + "' to " + keyCode);
|
||||||
|
prefsEditor.putString(functionName, String.valueOf(keyCode)).apply();
|
||||||
|
} else {
|
||||||
|
Logger.w(LOG_TAG,"Not setting a hotkey for invalid function: '" + functionName + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getKeyAddWord() {
|
public int getKeyAddWord() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_ADD_WORD);
|
return getFunctionKey(FUNC_ADD_WORD);
|
||||||
}
|
}
|
||||||
public int getKeyBackspace() {
|
public int getKeyBackspace() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_BACKSPACE);
|
return getFunctionKey(FUNC_BACKSPACE);
|
||||||
}
|
}
|
||||||
public int getKeyCommandPalette() {
|
public int getKeyCommandPalette() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_COMMAND_PALETTE);
|
return getFunctionKey(FUNC_COMMAND_PALETTE);
|
||||||
}
|
}
|
||||||
public int getKeyEditText() {
|
public int getKeyEditText() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_EDIT_TEXT);
|
return getFunctionKey(FUNC_EDIT_TEXT);
|
||||||
}
|
}
|
||||||
public int getKeyFilterClear() {
|
public int getKeyFilterClear() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_FILTER_CLEAR);
|
return getFunctionKey(FUNC_FILTER_CLEAR);
|
||||||
}
|
}
|
||||||
public int getKeyFilterSuggestions() {
|
public int getKeyFilterSuggestions() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_FILTER_SUGGESTIONS);
|
return getFunctionKey(FUNC_FILTER_SUGGESTIONS);
|
||||||
}
|
}
|
||||||
public int getKeyPreviousSuggestion() {
|
public int getKeyPreviousSuggestion() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_PREVIOUS_SUGGESTION);
|
return getFunctionKey(FUNC_PREVIOUS_SUGGESTION);
|
||||||
}
|
}
|
||||||
public int getKeyNextSuggestion() {
|
public int getKeyNextSuggestion() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_NEXT_SUGGESTION);
|
return getFunctionKey(FUNC_NEXT_SUGGESTION);
|
||||||
}
|
}
|
||||||
public int getKeyNextInputMode() {
|
public int getKeyNextInputMode() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_NEXT_INPUT_MODE);
|
return getFunctionKey(FUNC_NEXT_INPUT_MODE);
|
||||||
}
|
}
|
||||||
public int getKeyNextLanguage() {
|
public int getKeyNextLanguage() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_NEXT_LANGUAGE);
|
return getFunctionKey(FUNC_NEXT_LANGUAGE);
|
||||||
}
|
}
|
||||||
public int getKeySelectKeyboard() {
|
public int getKeySelectKeyboard() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_SELECT_KEYBOARD);
|
return getFunctionKey(FUNC_SELECT_KEYBOARD);
|
||||||
}
|
}
|
||||||
public int getKeyShift() {
|
public int getKeyShift() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_SHIFT);
|
return getFunctionKey(FUNC_SHIFT);
|
||||||
}
|
}
|
||||||
public int getKeySpaceKorean() {
|
public int getKeySpaceKorean() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_SPACE_KOREAN);
|
return getFunctionKey(FUNC_SPACE_KOREAN);
|
||||||
}
|
}
|
||||||
public int getKeyShowSettings() {
|
public int getKeyShowSettings() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_SHOW_SETTINGS);
|
return getFunctionKey(FUNC_SHOW_SETTINGS);
|
||||||
}
|
}
|
||||||
public int getKeyVoiceInput() {
|
public int getKeyVoiceInput() {
|
||||||
return getFunctionKey(SectionKeymap.ITEM_VOICE_INPUT);
|
return getFunctionKey(FUNC_VOICE_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getFunction(int keyCode) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : FUNCTIONS) {
|
||||||
|
if (keyCode == getFunctionKey(key)) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isValidFunction(String functionName) {
|
||||||
|
for (String validName : FUNCTIONS) {
|
||||||
|
if (validName.equals(functionName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@ import java.util.Set;
|
||||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||||
import io.github.sspanak.tt9.util.Logger;
|
import io.github.sspanak.tt9.util.Logger;
|
||||||
|
|
||||||
class SettingsInput extends SettingsHotkeys {
|
class SettingsInput extends SettingsHacks {
|
||||||
private final String LOG_TAG = SettingsInput.class.getSimpleName();
|
|
||||||
|
|
||||||
SettingsInput(Context context) { super(context); }
|
SettingsInput(Context context) { super(context); }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package io.github.sspanak.tt9.preferences.settings;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
|
||||||
public class SettingsStore extends SettingsUI {
|
public class SettingsStore extends SettingsHotkeys {
|
||||||
public SettingsStore(Context context) { super(context); }
|
public SettingsStore(Context context) { super(context); }
|
||||||
|
|
||||||
/************* internal settings *************/
|
/************* internal settings *************/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package io.github.sspanak.tt9.ui;
|
package io.github.sspanak.tt9.ui;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
@ -55,24 +57,28 @@ public class UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void confirm(Context context, String title, String message, String OKLabel, Runnable onOk, Runnable onCancel) {
|
public static void confirm(@NonNull Context context, String title, String message, String OKLabel, Runnable onOk, boolean cancelLabel, Runnable onCancel, DialogInterface.OnKeyListener onKey) {
|
||||||
|
Dialog dialogue;
|
||||||
|
|
||||||
if (DeviceInfo.AT_LEAST_ANDROID_12) {
|
if (DeviceInfo.AT_LEAST_ANDROID_12) {
|
||||||
new MaterialAlertDialogBuilder(context)
|
dialogue = new MaterialAlertDialogBuilder(context)
|
||||||
.setTitle(title)
|
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })
|
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })
|
||||||
.setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); })
|
.setNegativeButton(cancelLabel ? context.getString(android.R.string.cancel) : null, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); })
|
||||||
.setCancelable(false)
|
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
new AlertDialog.Builder(context)
|
dialogue = new AlertDialog.Builder(context)
|
||||||
.setTitle(title)
|
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })
|
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })
|
||||||
.setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); })
|
.setNegativeButton(cancelLabel ? context.getString(android.R.string.cancel) : null, (dialog, whichButton) -> { if (onCancel != null) onCancel.run(); })
|
||||||
.setCancelable(false)
|
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialogue.setTitle(title);
|
||||||
|
dialogue.setCancelable(false);
|
||||||
|
if (onKey != null) {
|
||||||
|
dialogue.setOnKeyListener(onKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ abstract public class PopupDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void render(Runnable OKAction) {
|
protected void render(Runnable OKAction) {
|
||||||
UI.confirm(context, title, message, OKLabel, OKAction, () -> activityFinisher.accept(""));
|
UI.confirm(context, title, message, OKLabel, OKAction, true, () -> activityFinisher.accept(""), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void render();
|
abstract void render();
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,10 @@
|
||||||
<string name="donate_hold_to_open">Press and hold to open in the browser.</string>
|
<string name="donate_hold_to_open">Press and hold to open in the browser.</string>
|
||||||
<string name="donate_url" translatable="false">https://www.buymeacoffee.com/sspanak</string>
|
<string name="donate_url" translatable="false">https://www.buymeacoffee.com/sspanak</string>
|
||||||
|
|
||||||
|
<string name="function_already_assigned">Key \"%1$s\" is already assigned to function \"%2$s\". Do you want to assign it to \"%3$s\"?</string>
|
||||||
|
<string name="function_assign_instructions">* Press or hold a key to assign it to the \"%1$s\" function.\n\n* Press 0 to cancel.\n\n* Press 2 to disable the function.</string>
|
||||||
|
<string name="function_reassign">Reassign</string>
|
||||||
|
|
||||||
<string name="function_add_word">Add Word</string>
|
<string name="function_add_word">Add Word</string>
|
||||||
<string name="function_add_word_not_available">Adding words is not possible in this language.</string>
|
<string name="function_add_word_not_available">Adding words is not possible in this language.</string>
|
||||||
<string name="function_backspace">Backspace</string>
|
<string name="function_backspace">Backspace</string>
|
||||||
|
|
@ -195,6 +199,7 @@
|
||||||
<string name="setup_spell_checker_on">System spell checker may interfere with adding words. Click here to disable.</string>
|
<string name="setup_spell_checker_on">System spell checker may interfere with adding words. Click here to disable.</string>
|
||||||
<string name="setup_spell_checker_off">Disabled</string>
|
<string name="setup_spell_checker_off">Disabled</string>
|
||||||
|
|
||||||
|
<string name="key_key">Key</string>
|
||||||
<string name="key_hold_key">(hold)</string>
|
<string name="key_hold_key">(hold)</string>
|
||||||
<string name="key_none" translatable="false">--</string>
|
<string name="key_none" translatable="false">--</string>
|
||||||
<string name="key_back">Back</string>
|
<string name="key_back">Back</string>
|
||||||
|
|
|
||||||
|
|
@ -2,63 +2,63 @@
|
||||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
app:orderingFromXml="true">
|
app:orderingFromXml="true">
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_add_word"
|
app:key="key_add_word"
|
||||||
app:title="@string/function_add_word" />
|
app:title="@string/function_add_word" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceBackspaceHotkey
|
||||||
app:key="key_backspace"
|
app:key="key_backspace"
|
||||||
app:title="@string/function_backspace" />
|
app:title="@string/function_backspace" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_command_palette"
|
app:key="key_command_palette"
|
||||||
app:title="@string/function_show_command_palette" />
|
app:title="@string/function_show_command_palette" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_filter_clear"
|
app:key="key_filter_clear"
|
||||||
app:title="@string/function_filter_clear" />
|
app:title="@string/function_filter_clear" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_edit_text"
|
app:key="key_edit_text"
|
||||||
app:title="@string/function_edit_text" />
|
app:title="@string/function_edit_text" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_filter_suggestions"
|
app:key="key_filter_suggestions"
|
||||||
app:title="@string/function_filter_suggestions" />
|
app:title="@string/function_filter_suggestions" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_previous_suggestion"
|
app:key="key_previous_suggestion"
|
||||||
app:title="@string/function_previous_suggestion" />
|
app:title="@string/function_previous_suggestion" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_next_suggestion"
|
app:key="key_next_suggestion"
|
||||||
app:title="@string/function_next_suggestion" />
|
app:title="@string/function_next_suggestion" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_next_language"
|
app:key="key_next_language"
|
||||||
app:title="@string/function_next_language" />
|
app:title="@string/function_next_language" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_next_input_mode"
|
app:key="key_next_input_mode"
|
||||||
app:title="@string/function_next_mode" />
|
app:title="@string/function_next_mode" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_select_keyboard"
|
app:key="key_select_keyboard"
|
||||||
app:title="@string/function_select_keyboard" />
|
app:title="@string/function_select_keyboard" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_shift"
|
app:key="key_shift"
|
||||||
app:title="@string/virtual_key_shift" />
|
app:title="@string/virtual_key_shift" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_space_korean"
|
app:key="key_space_korean"
|
||||||
app:title="@string/virtual_key_space_korean" />
|
app:title="@string/virtual_key_space_korean" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceHotkey
|
||||||
app:key="key_show_settings"
|
app:key="key_show_settings"
|
||||||
app:title="@string/function_show_settings" />
|
app:title="@string/function_show_settings" />
|
||||||
|
|
||||||
<DropDownPreference
|
<io.github.sspanak.tt9.preferences.screens.hotkeys.PreferenceVoiceInputHotkey
|
||||||
app:key="key_voice_input"
|
app:key="key_voice_input"
|
||||||
app:title="@string/function_voice_input" />
|
app:title="@string/function_voice_input" />
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue