Don't remember
This commit is contained in:
@@ -28,22 +28,23 @@ import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// TODO: Implement auto-correct.
|
||||
// TODO: Implement auto-caps.
|
||||
// TODO: Implement device recognition and a keymap picking based on it.
|
||||
// TODO: Mode without the bottom input view, in case the user does not care about completions.
|
||||
// This will involve making a popup screen and a shortcut for changing languages.
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InputMethod extends InputMethodService {
|
||||
public MetaKeyManager mMetaKeyManager;
|
||||
public KeymapManager mKeymapManager;
|
||||
public InputView mInputView;
|
||||
public Settings mSettings;
|
||||
public int mCurrentCursorPosition;
|
||||
private SymbolCycler mSymbolCycler;
|
||||
private long mLastKeyTime;
|
||||
private int mRegularKeyPressed = -1;
|
||||
private int mSymbolPressed;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -75,12 +76,6 @@ public class InputMethod extends InputMethodService {
|
||||
mMetaKeyManager.updateMetaStateDown(keyCode);
|
||||
mInputView.updateStateText();
|
||||
|
||||
InputConnection conn = getCurrentInputConnection();
|
||||
|
||||
if (conn == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
if (isInputViewShown()) {
|
||||
@@ -93,13 +88,7 @@ public class InputMethod extends InputMethodService {
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
return false;
|
||||
case KeyEvent.KEYCODE_SPACE:
|
||||
if (mSettings.isCyclingWithSpaceEnabled() && mRegularKeyPressed != -1
|
||||
&& event.getEventTime() - mLastKeyTime > 200) {
|
||||
mSymbolCycler.cycle();
|
||||
} else {
|
||||
conn.commitText(" ", 1);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
}
|
||||
handleSpacebar(event);
|
||||
return true;
|
||||
default:
|
||||
ArrayList<String> symbols = mKeymapManager.getSymbols(keyCode, mMetaKeyManager);
|
||||
@@ -109,11 +98,13 @@ public class InputMethod extends InputMethodService {
|
||||
}
|
||||
|
||||
if (event.getRepeatCount() == 0 && !symbols.isEmpty()) {
|
||||
conn.commitText(symbols.get(0), 1);
|
||||
mRegularKeyPressed = keyCode;
|
||||
getCurrentInputConnection().commitText(symbols.get(0), 1);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
mInputView.updateStateText();
|
||||
mSymbolPressed = keyCode;
|
||||
mLastKeyTime = event.getEventTime();
|
||||
mSymbolCycler.stopCycling();
|
||||
mSymbolCycler.startCycling(symbols, conn);
|
||||
mSymbolCycler.startCycling(symbols, getCurrentInputConnection());
|
||||
if (!mSettings.isCyclingWithSpaceEnabled()) {
|
||||
mSymbolCycler.startAutomaticCycling();
|
||||
}
|
||||
@@ -123,27 +114,86 @@ public class InputMethod extends InputMethodService {
|
||||
}
|
||||
}
|
||||
|
||||
public void handleSpacebar(KeyEvent event) {
|
||||
InputConnection conn = getCurrentInputConnection();
|
||||
|
||||
if (mSettings.isCyclingWithSpaceEnabled()
|
||||
&& mSymbolPressed != -1 && event.getEventTime() - mLastKeyTime > 200) {
|
||||
mSymbolCycler.cycle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSettings.isAutoPeriodsEnabled()) {
|
||||
String text = getTextBeforeCursor(2);
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
Pattern pattern = Pattern.compile("[^\\.… ]+ $");
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if (matcher.find()) {
|
||||
// InputConnection.replaceSelection needs a higher API, so we just do exactly
|
||||
// what it does to support more devices.
|
||||
conn.finishComposingText();
|
||||
conn.setSelection(mCurrentCursorPosition - 1, mCurrentCursorPosition);
|
||||
conn.commitText(".", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.commitText(" ", 1);
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
}
|
||||
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (mRegularKeyPressed == keyCode) {
|
||||
mMetaKeyManager.adjustMetaStateAfterKeypress();
|
||||
if (mSymbolPressed == keyCode) {
|
||||
mSymbolPressed = -1;
|
||||
mSymbolCycler.stopCycling();
|
||||
mRegularKeyPressed = -1;
|
||||
mInputView.updateSymbolsView(null, 0);
|
||||
}
|
||||
|
||||
InputConnection conn = getCurrentInputConnection();
|
||||
|
||||
if (conn == null) {
|
||||
return false;
|
||||
// TODO: Not ideal. Should be handled on key down ideally.
|
||||
if (keyCode == KeyEvent.KEYCODE_DEL) {
|
||||
detectAutoCapitalization();
|
||||
}
|
||||
|
||||
conn.finishComposingText();
|
||||
getCurrentInputConnection().finishComposingText();
|
||||
mMetaKeyManager.updateMetaStateUp(keyCode, event);
|
||||
mInputView.updateStateText();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
|
||||
int candidatesStart, int candidatesEnd) {
|
||||
mCurrentCursorPosition = newSelStart;
|
||||
detectAutoCapitalization();
|
||||
}
|
||||
|
||||
public String getTextBeforeCursor(int n) {
|
||||
CharSequence chars = getCurrentInputConnection().getTextBeforeCursor(n, 0);
|
||||
return chars == null ? "" : String.valueOf(chars);
|
||||
}
|
||||
|
||||
public void detectAutoCapitalization() {
|
||||
if (!mSettings.isAutoCapsEnabled()) {
|
||||
return;
|
||||
}
|
||||
// Get a bunch of text, run a regex through it, and bam. Is there a better way to do this?
|
||||
String text = getTextBeforeCursor(16);
|
||||
if (text.isEmpty()) {
|
||||
mMetaKeyManager.getShift().setLatched(true);
|
||||
mInputView.updateStateText();
|
||||
} else {
|
||||
// If, on the other hand, we have some characters before the cursor, we scan.
|
||||
Pattern pattern = Pattern.compile("[\\.…\\!\\?] +$");
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if (matcher.find()) {
|
||||
mMetaKeyManager.getShift().setLatched(true);
|
||||
mInputView.updateStateText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateInputView() {
|
||||
return mInputView;
|
||||
@@ -152,6 +202,8 @@ public class InputMethod extends InputMethodService {
|
||||
@Override
|
||||
public void onStartInput(EditorInfo info, boolean restarting) {
|
||||
super.onStartInput(info, restarting);
|
||||
|
||||
detectAutoCapitalization();
|
||||
mKeymapManager.checkCurrentKeymap(mSettings.getSelectedKeymaps());
|
||||
mInputView.updateStateText();
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ package cfpi_fpsi.eu.titanboard;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -55,7 +53,6 @@ public class SymbolCycler extends Handler {
|
||||
removeCallbacks(mRunnable);
|
||||
}
|
||||
|
||||
|
||||
public void cycle() {
|
||||
mIndex++;
|
||||
if (mIndex == mSymbols.size()) {
|
||||
@@ -63,11 +60,8 @@ public class SymbolCycler extends Handler {
|
||||
}
|
||||
|
||||
mInputMethod.mInputView.updateSymbolsView(mSymbols, mIndex);
|
||||
ExtractedText text = mConn.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
if (text != null) {
|
||||
mConn.setComposingRegion(text.selectionStart - 1, text.selectionStart);
|
||||
mConn.setComposingText(mSymbols.get(mIndex), 1);
|
||||
}
|
||||
mConn.setComposingRegion(mInputMethod.mCurrentCursorPosition - 1, mInputMethod.mCurrentCursorPosition);
|
||||
mConn.setComposingText(mSymbols.get(mIndex), 1);
|
||||
}
|
||||
|
||||
private class SymbolCyclerRunnable implements Runnable {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* An input method for the Unihertz Titan/physical keyboard phones
|
||||
* Copyright (C) 2024 Emil Kosz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package cfpi_fpsi.eu.titanboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class SymbolsView extends RelativeLayout {
|
||||
public SymbolsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ An input method for the Unihertz Titan/physical keyboard phones
|
||||
~ Copyright (C) 2024 Emil Kosz
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromDegrees="45"
|
||||
android:pivotX="100%"
|
||||
android:pivotY="-50%">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#333" />
|
||||
</shape>
|
||||
</rotate>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ An input method for the Unihertz Titan/physical keyboard phones
|
||||
~ Copyright (C) 2024 Emil Kosz
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<cfpi_fpsi.eu.titanboard.SymbolsView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_below="@id/textView"
|
||||
android:src="@drawable/triangle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
style="@style/SymbolsViewSymbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="A" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
style="@style/SymbolsViewSymbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/textView"
|
||||
android:text="B" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
style="@style/SymbolsViewSymbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/textView2"
|
||||
android:text="C" />
|
||||
|
||||
</cfpi_fpsi.eu.titanboard.SymbolsView>
|
||||
@@ -20,6 +20,6 @@
|
||||
<resources>
|
||||
<bool name="auto_caps_default">true</bool>
|
||||
<bool name="auto_periods_default">true</bool>
|
||||
<bool name="autocorrect_default">true</bool>
|
||||
<bool name="autocorrect_default">false</bool>
|
||||
<bool name="cycle_with_space_default">false</bool>
|
||||
</resources>
|
||||
@@ -3,10 +3,12 @@
|
||||
<string name="ime_label">Titanboard</string>
|
||||
|
||||
<string name="preferences_label">Titanboard preferences</string>
|
||||
<string name="preferences_cycle_speed">Cycling speed</string>
|
||||
<string name="preferences_general_settings">General</string>
|
||||
<string name="preferences_auto_caps">Automatic capitalization</string>
|
||||
<string name="preferences_auto_periods">Automatic periods</string>
|
||||
<string name="preferences_auto_correct">Autocorrect</string>
|
||||
<string name="preferences_editing_settings">Editing and keymaps</string>
|
||||
<string name="preferences_cycle_speed">Cycling speed</string>
|
||||
<string name="preferences_cycle_with_space">Cycle symbols with the space key</string>
|
||||
<string name="preferences_keymaps">Keymaps</string>
|
||||
|
||||
|
||||
@@ -32,4 +32,13 @@
|
||||
<item name="android:textColor">#DDD</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="SymbolsViewSymbol">
|
||||
<item name="android:background">#333</item>
|
||||
<item name="android:textColor">#AAA</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textAlignment">center</item>
|
||||
<item name="android:padding">8dp</item>
|
||||
<item name="android:layout_gravity">center_vertical</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -746,7 +746,7 @@
|
||||
<alt value="!" />
|
||||
</key>
|
||||
</keymap>
|
||||
<!-- Greek -->
|
||||
<!-- Modern Greek, monotonic orthography -->
|
||||
<!-- https://en.wikipedia.org/wiki/List_of_QWERTY_keyboard_language_variants#/media/File:Greek_keyboard_mono_3b.png -->
|
||||
<!-- TODO: add diacritics -->
|
||||
<keymap
|
||||
@@ -755,7 +755,9 @@
|
||||
locked-name="EL">
|
||||
<key code="29"> <!-- A -->
|
||||
<base value="α" />
|
||||
<base value="ά" />
|
||||
<shift value="Α" />
|
||||
<shift value="Ά" />
|
||||
<alt value="@" />
|
||||
</key>
|
||||
<key code="30"> <!-- B -->
|
||||
@@ -780,7 +782,9 @@
|
||||
</key>
|
||||
<key code="33"> <!-- E -->
|
||||
<base value="ε" />
|
||||
<base value="έ" />
|
||||
<shift value="E" />
|
||||
<shift value="Έ" />
|
||||
<alt value="_" />
|
||||
<alt value="~" />
|
||||
</key>
|
||||
@@ -798,7 +802,9 @@
|
||||
</key>
|
||||
<key code="36"> <!-- H -->
|
||||
<base value="η" />
|
||||
<base value="ή" />
|
||||
<shift value="H" />
|
||||
<shift value="Ή" />
|
||||
<alt value="#" />
|
||||
<alt value="₽" />
|
||||
<alt value="€" />
|
||||
@@ -806,7 +812,12 @@
|
||||
</key>
|
||||
<key code="37"> <!-- I -->
|
||||
<base value="ι" />
|
||||
<base value="ί" />
|
||||
<base value="ϊ" />
|
||||
<base value="ΐ" />
|
||||
<shift value="I" />
|
||||
<shift value="Ί" />
|
||||
<shift value="Ϊ" />
|
||||
<alt value="2" />
|
||||
</key>
|
||||
<key code="38"> <!-- J -->
|
||||
@@ -834,9 +845,12 @@
|
||||
<shift value="Ν" />
|
||||
<alt value="8" />
|
||||
</key>
|
||||
<!-- Lots of diacritics -->
|
||||
<key code="43"> <!-- O -->
|
||||
<base value="o" />
|
||||
<base value="ό" />
|
||||
<shift value="O" />
|
||||
<shift value="Ό" />
|
||||
<alt value="3" />
|
||||
</key>
|
||||
<key code="44"> <!-- P -->
|
||||
@@ -850,6 +864,7 @@
|
||||
<alt value=":" />
|
||||
<alt value=";" />
|
||||
</key>
|
||||
<!-- One diacritic -->
|
||||
<key code="46"> <!-- R -->
|
||||
<base value="ρ" />
|
||||
<shift value="Ρ" />
|
||||
@@ -877,9 +892,12 @@
|
||||
<shift value="Θ" />
|
||||
<alt value="1" />
|
||||
</key>
|
||||
<!-- Lots of diacritics -->
|
||||
<key code="50"> <!-- V -->
|
||||
<base value="ω" />
|
||||
<base value="ώ" />
|
||||
<shift value="Ω" />
|
||||
<shift value="Ώ" />
|
||||
<alt value="." />
|
||||
<alt value="…" />
|
||||
</key>
|
||||
@@ -894,9 +912,15 @@
|
||||
<shift value="X" />
|
||||
<alt value="?" />
|
||||
</key>
|
||||
<!-- Lots of diacritics -->
|
||||
<key code="53"> <!-- Y -->
|
||||
<base value="υ" />
|
||||
<base value="ύ" />
|
||||
<base value="ϋ" />
|
||||
<base value="ΰ" />
|
||||
<shift value="Y" />
|
||||
<shift value="Ύ" />
|
||||
<shift value="Ϋ" />
|
||||
<alt value=")" />
|
||||
<alt value=">" />
|
||||
<alt value="]" />
|
||||
|
||||
@@ -19,32 +19,39 @@
|
||||
|
||||
<!--suppress DeprecatedClassUsageInspection -->
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/auto_caps_default"
|
||||
android:key="auto_caps"
|
||||
android:title="@string/preferences_auto_caps" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/auto_periods_default"
|
||||
android:key="auto_periods"
|
||||
android:title="@string/preferences_auto_periods" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/autocorrect_default"
|
||||
android:key="autocorrect"
|
||||
android:title="@string/preferences_auto_correct" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/cycle_with_space_default"
|
||||
android:key="cycle_with_space"
|
||||
android:title="@string/preferences_cycle_with_space" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/cycle_speed_default"
|
||||
android:entries="@array/cycle_speed_labels"
|
||||
android:entryValues="@array/cycle_speeds"
|
||||
android:key="cycle_speed"
|
||||
android:title="@string/preferences_cycle_speed" />
|
||||
<MultiSelectListPreference
|
||||
android:defaultValue="@array/selected_keymap_defaults"
|
||||
android:entries="@array/available_keymap_labels"
|
||||
android:entryValues="@array/available_keymaps"
|
||||
android:key="selected_keymaps"
|
||||
android:title="@string/preferences_keymaps" />
|
||||
<PreferenceCategory android:title="@string/preferences_general_settings">
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/auto_caps_default"
|
||||
android:key="auto_caps"
|
||||
android:title="@string/preferences_auto_caps" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/auto_periods_default"
|
||||
android:key="auto_periods"
|
||||
android:title="@string/preferences_auto_periods" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/autocorrect_default"
|
||||
android:enabled="false"
|
||||
android:key="autocorrect"
|
||||
android:title="@string/preferences_auto_correct" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/preferences_editing_settings">
|
||||
|
||||
<MultiSelectListPreference
|
||||
android:defaultValue="@array/selected_keymap_defaults"
|
||||
android:entries="@array/available_keymap_labels"
|
||||
android:entryValues="@array/available_keymaps"
|
||||
android:key="selected_keymaps"
|
||||
android:title="@string/preferences_keymaps" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="@bool/cycle_with_space_default"
|
||||
android:key="cycle_with_space"
|
||||
android:title="@string/preferences_cycle_with_space" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/cycle_speed_default"
|
||||
android:entries="@array/cycle_speed_labels"
|
||||
android:entryValues="@array/cycle_speeds"
|
||||
android:key="cycle_speed"
|
||||
android:title="@string/preferences_cycle_speed" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user