Compare commits

...

5 Commits

  1. 6
      8vim/build.gradle
  2. 364123
      8vim/src/main/assets/corpus.8vim
  3. 20
      8vim/src/main/java/inc/flide/vim8/keyboardActionListners/KeypadActionListener.java
  4. 17
      8vim/src/main/java/inc/flide/vim8/keyboardActionListners/MainKeypadActionListener.java
  5. 8
      8vim/src/main/java/inc/flide/vim8/keyboardHelpers/InputMethodServiceHelper.java
  6. 15
      8vim/src/main/java/inc/flide/vim8/keyboardHelpers/KeyboardDataXmlParser.java
  7. 9
      8vim/src/main/java/inc/flide/vim8/structures/KeyboardAction.java
  8. 2
      8vim/src/main/java/inc/flide/vim8/structures/KeyboardActionType.java
  9. 33
      8vim/src/main/java/inc/flide/vim8/views/mainKeyboard/MainKeyboardView.java
  10. 33
      8vim/src/main/java/inc/flide/vim8/views/mainKeyboard/XpadView.java
  11. 16
      8vim/src/main/res/raw/completion_actions.xml
  12. 47
      corpus/src/main/java/com/dfsek/corpus/Analysis.java
  13. 40
      corpus/src/main/java/com/dfsek/corpus/Read.java

@ -9,8 +9,8 @@ android {
defaultConfig {
applicationId "inc.flide.vi8"
minSdkVersion 21
targetSdkVersion 30
minSdkVersion 24
targetSdkVersion 33
versionCode 9
versionName "iteration-9"
vectorDrawables.useSupportLibrary = true
@ -42,5 +42,7 @@ dependencies {
implementation 'com.github.kizitonwose.colorpreference:support:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'org.apache.commons:commons-text:1.7'
implementation 'io.vavr:vavr:0.10.4'
implementation 'commons-io:commons-io:2.11.0'
api 'com.afollestad.material-dialogs:core:0.9.2.3'
}

File diff suppressed because it is too large Load Diff

@ -12,6 +12,9 @@ import inc.flide.vim8.structures.FingerPosition;
import inc.flide.vim8.structures.KeyboardAction;
import inc.flide.vim8.preferences.SharedPreferenceHelper;
import inc.flide.vim8.structures.CustomKeycode;
import inc.flide.vim8.views.mainKeyboard.MainKeyboardView;
import io.vavr.collection.List;
import io.vavr.collection.Stream;
public class KeypadActionListener {
@ -38,6 +41,23 @@ public class KeypadActionListener {
handleInputKey(keyboardAction.getKeyEventCode(), keyboardAction.getKeyFlags());
}
public void handleCompetion(KeyboardAction keyboardAction) {
List<String> completions = MainKeyboardView.completions.get(getLastWord()).getOrElse(List.empty());
if(completions.size() > keyboardAction.getCompletionIndex()) {
onText(completions.get(keyboardAction.getCompletionIndex()));
}
}
public String getLastWord() {
CharSequence textBeforeCursor = mainInputMethodService.getCurrentInputConnection().getTextBeforeCursor(20, 0);
if(textBeforeCursor == null) return "";
return Stream.ofAll(textBeforeCursor.chars().boxed())
.foldLeft("", (run, c) -> {
if(Character.isWhitespace(c)) return "";
return run + ((char) c.intValue());
});
}
public void handleInputKey(int keyCode, int keyFlags) {
boolean actionHandled = handleKeyEventKeyCodes(keyCode, keyFlags);

@ -4,8 +4,13 @@ import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@ -16,6 +21,13 @@ import inc.flide.vim8.structures.KeyboardData;
import inc.flide.vim8.structures.Constants;
import inc.flide.vim8.structures.FingerPosition;
import inc.flide.vim8.structures.MovementSequenceType;
import io.vavr.API;
import io.vavr.collection.HashMap;
import io.vavr.collection.Map;
import io.vavr.collection.Stream;
import org.apache.commons.io.IOUtils;
import static io.vavr.API.Tuple;
public class MainKeypadActionListener extends KeypadActionListener {
@ -27,6 +39,8 @@ public class MainKeypadActionListener extends KeypadActionListener {
private boolean isLongPressCallbackSet;
private MovementSequenceType currentMovementSequenceType = MovementSequenceType.NO_MOVEMENT;
private final Runnable longPressRunnable = new Runnable() {
@Override
public void run() {
@ -162,6 +176,9 @@ public class MainKeypadActionListener extends KeypadActionListener {
case INPUT_KEY:
handleInputKey(keyboardAction);
break;
case INPUT_COMPLETE:
handleCompetion(keyboardAction);
break;
}
}
}

@ -77,11 +77,15 @@ public final class InputMethodServiceHelper {
addToKeyboardActionsMapUsingResourceId(
layoutIndependentKeyboardData,
resources,
R.raw.special_core_gestures);
R.raw.completion_actions);
addToKeyboardActionsMapUsingResourceId(
layoutIndependentKeyboardData,
resources,
R.raw.zero_turn_no_actions);
R.raw.special_core_gestures);
//addToKeyboardActionsMapUsingResourceId(
// layoutIndependentKeyboardData,
// resources,
// R.raw.zero_turn_no_actions);
return layoutIndependentKeyboardData;
}

@ -25,6 +25,7 @@ class KeyboardDataXmlParser {
private static final String KEYBOARD_ACTION_MAP_TAG = "keyboardActionMap";
private static final String KEYBOARD_ACTION_TAG = "keyboardAction";
private static final String KEYBOARD_ACTION_TYPE_TAG = "keyboardActionType";
private static final String COMPLETION_TAG = "completion";
private static final String MOVEMENT_SEQUENCE_TAG = "movementSequence";
private static final String INPUT_STRING_TAG = "inputString";
private static final String INPUT_CAPSLOCK_STRING_TAG = "inputCapsLockString";
@ -111,6 +112,7 @@ class KeyboardDataXmlParser {
String associatedCapsLockText = "";
int keyEventCode = 0;
int flags = 0;
int completionIndex = -1;
while (parser.next() != XmlPullParser.END_TAG) {
@ -138,12 +140,15 @@ class KeyboardDataXmlParser {
case INPUT_KEY_FLAGS_TAG:
flags = readInputFlags();
break;
case COMPLETION_TAG:
completionIndex = readCompletionIndex();
break;
default:
//Logger.w(this, "keyboard_actions xml has unknown tag : " + tagName);
}
}
keyboardAction = new KeyboardAction(keyboardActionType, associatedText, associatedCapsLockText, keyEventCode, flags);
keyboardAction = new KeyboardAction(keyboardActionType, associatedText, associatedCapsLockText, keyEventCode, flags, completionIndex);
return new AbstractMap.SimpleEntry<>(movementSequence, keyboardAction);
}
@ -196,6 +201,14 @@ class KeyboardDataXmlParser {
return keyCode;
}
private int readCompletionIndex() throws IOException, XmlPullParserException {
parser.require(XmlPullParser.START_TAG, null, COMPLETION_TAG);
String inputKeyString = readText();
parser.require(XmlPullParser.END_TAG, null, COMPLETION_TAG);
return Integer.parseInt(inputKeyString);
}
private String readInputString() throws IOException, XmlPullParserException {
parser.require(XmlPullParser.START_TAG, null, INPUT_STRING_TAG);
String inputString = readText();

@ -10,10 +10,13 @@ public class KeyboardAction {
private final int keyEventCode;
private final int keyFlags;
public KeyboardAction(KeyboardActionType keyboardActionType, String text, String capsLockText, int keyEventCode, int keyFlags) {
private final int completionIndex;
public KeyboardAction(KeyboardActionType keyboardActionType, String text, String capsLockText, int keyEventCode, int keyFlags, int completionIndex) {
this.keyboardActionType = keyboardActionType;
this.text = text;
this.keyEventCode = keyEventCode;
this.completionIndex = completionIndex;
setCapsLockText(capsLockText);
this.keyFlags = keyFlags;
}
@ -38,6 +41,10 @@ public class KeyboardAction {
return capsLockText;
}
public int getCompletionIndex() {
return completionIndex;
}
private void setCapsLockText(String capsLockText) {
if ((capsLockText == null || capsLockText.length() == 0) && this.text != null) {
this.capsLockText = this.text.toUpperCase(Locale.getDefault());

@ -1,5 +1,5 @@
package inc.flide.vim8.structures;
public enum KeyboardActionType {
INPUT_TEXT, INPUT_KEY
INPUT_TEXT, INPUT_KEY, INPUT_COMPLETE
}

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.widget.ImageButton;
@ -20,12 +21,27 @@ import inc.flide.vim8.preferences.SharedPreferenceHelper;
import inc.flide.vim8.structures.CustomKeycode;
import inc.flide.vim8.structures.KeyboardActionType;
import inc.flide.vim8.ui.SettingsActivity;
import io.vavr.API;
import io.vavr.collection.HashMap;
import io.vavr.collection.List;
import io.vavr.collection.Map;
import io.vavr.collection.Stream;
import org.apache.commons.io.IOUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import static io.vavr.API.Tuple;
public class MainKeyboardView extends ConstraintLayout {
private MainKeypadActionListener actionListener;
public static Map<String, List<String>> completions = API.Map();
public MainKeyboardView(Context context) {
super(context);
initialize(context);
@ -42,6 +58,19 @@ public class MainKeyboardView extends ConstraintLayout {
}
public void initialize(Context context) {
if(completions.isEmpty()) {
new Thread(() -> {
Log.i("Corpus", "Parsing corpus...");
try {
completions = Stream.ofAll(new BufferedReader(new StringReader(IOUtils.toString(context.getAssets().open("corpus.8vim"), Charset.defaultCharset()))).lines())
.map(s -> Tuple(s.substring(0, s.indexOf(':')), s.substring(s.indexOf(':') + 1)))
.foldLeft(HashMap.empty(), (run, tup) -> run.put(tup._1(), API.List(tup._2()), io.vavr.collection.List::appendAll));
Log.i("Corpus", "Done.");
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
}
actionListener = new MainKeypadActionListener((MainInputMethodService) context, this);
setupMainKeyboardView(context);
setupButtonsOnSideBar();
@ -125,7 +154,7 @@ public class MainKeyboardView extends ConstraintLayout {
"",
null,
CustomKeycode.SWITCH_TO_SELECTION_KEYPAD.getKeyCode(),
0);
0, -1);
actionListener.handleInputKey(switchToSelectionKeyboard);
});
}
@ -138,7 +167,7 @@ public class MainKeyboardView extends ConstraintLayout {
"",
null,
CustomKeycode.SWITCH_TO_EMOTICON_KEYBOARD.getKeyCode(),
0);
0, -1);
actionListener.handleInputKey(switchToEmojiKeyboard);
});
}

@ -27,6 +27,8 @@ import inc.flide.vim8.geometry.Dimension;
import inc.flide.vim8.keyboardActionListners.MainKeypadActionListener;
import inc.flide.vim8.preferences.SharedPreferenceHelper;
import inc.flide.vim8.structures.FingerPosition;
import io.vavr.collection.List;
import io.vavr.control.Option;
public class XpadView extends View {
private final Random rnd = new Random();
@ -241,6 +243,8 @@ public class XpadView extends View {
setupSectorIcons(centreXValue, centreYValue, canvas);
}
setupCompletions(centreXValue, centreYValue, canvas);
//the text along the lines
boolean userPreferWheelLetters = SharedPreferenceHelper
.getInstance(getContext())
@ -336,6 +340,35 @@ public class XpadView extends View {
foregroundBoldPaint.setTypeface(fontBold);
}
private void setupCompletions(int centerXValue, int centerYValue, Canvas canvas) {
float density = getResources().getDisplayMetrics().density;
foregroundPaint.setStrokeWidth(0.75f * density);
foregroundPaint.setStyle(Paint.Style.FILL);
foregroundPaint.setTextAlign(Paint.Align.CENTER);
int iconCenterX = (int) Math.max(sectorLineBounds.left, 0);
int iconCenterY = centerYValue;
String lastWord = actionListener.getLastWord();
List<String> completions = MainKeyboardView.completions.get(lastWord).getOrElse(List.empty());
String completion1 = completions.size() > 0 ? completions.get(0) : "";
String completion2 = completions.size() > 1 ? completions.get(1) : "";
String completion3 = completions.size() > 2 ? completions.get(2) : "";
String completion4 = completions.size() > 3 ? completions.get(3) : "";
canvas.drawText(completion1, iconCenterX, iconCenterY, foregroundPaint);
iconCenterX = (int) Math.min(sectorLineBounds.right, canvas.getWidth());
canvas.drawText(completion2, iconCenterX, iconCenterY, foregroundPaint);
iconCenterX = centerXValue;
iconCenterY = (int) Math.min(sectorLineBounds.bottom, canvas.getHeight());
canvas.drawText(completion3, iconCenterX, iconCenterY, foregroundPaint);
iconCenterY = (int) Math.max(sectorLineBounds.top, 0);
canvas.drawText(completion4, iconCenterX, iconCenterY, foregroundPaint);
}
private void setupSectorIcons(int centreXValue, int centreYValue, Canvas canvas) {
int iconSize = getResources().getDimensionPixelSize(R.dimen.icon_size);

@ -1,24 +1,24 @@
<keyboardData>
<keyboardActionMap>
<keyboardAction>
<keyboardActionType>INPUT_KEY</keyboardActionType>
<keyboardActionType>INPUT_COMPLETE</keyboardActionType>
<movementSequence>INSIDE_CIRCLE;TOP;INSIDE_CIRCLE;</movementSequence>
<inputKey>NO_OPERATION</inputKey>
<completion>3</completion>
</keyboardAction>
<keyboardAction>
<keyboardActionType>INPUT_KEY</keyboardActionType>
<keyboardActionType>INPUT_COMPLETE</keyboardActionType>
<movementSequence>INSIDE_CIRCLE;BOTTOM;INSIDE_CIRCLE;</movementSequence>
<inputKey>NO_OPERATION</inputKey>
<completion>2</completion>
</keyboardAction>
<keyboardAction>
<keyboardActionType>INPUT_KEY</keyboardActionType>
<keyboardActionType>INPUT_COMPLETE</keyboardActionType>
<movementSequence>INSIDE_CIRCLE;LEFT;INSIDE_CIRCLE;</movementSequence>
<inputKey>NO_OPERATION</inputKey>
<completion>0</completion>
</keyboardAction>
<keyboardAction>
<keyboardActionType>INPUT_KEY</keyboardActionType>
<keyboardActionType>INPUT_COMPLETE</keyboardActionType>
<movementSequence>INSIDE_CIRCLE;RIGHT;INSIDE_CIRCLE;</movementSequence>
<inputKey>NO_OPERATION</inputKey>
<completion>1</completion>
</keyboardAction>
</keyboardActionMap>
</keyboardData>

@ -6,8 +6,7 @@ import io.vavr.Tuple2;
import io.vavr.collection.*;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Comparator;
import java.util.Locale;
@ -25,14 +24,19 @@ public class Analysis {
private static final String punctuation = "!,.;:";
public static void main(String... args) throws IOException {
String in = IOUtils.resourceToString("/corpus.txt", Charset.defaultCharset());
try(InputStream is = Analysis.class.getResourceAsStream("/corpus.txt")) {
HashSet<Character> keyChars = HashSet.ofAll(keys.toCharArray());
HashSet<Character> punctuationChars = HashSet.ofAll(punctuation.toCharArray());
Map<String, Integer> rawTokens = Stream.ofAll(in.chars().boxed())
.filter(c -> !punctuationChars.contains((char) c.intValue()))
Map<String, Integer> rawTokens = Stream.ofAll(new BufferedReader(new InputStreamReader(is))
.lines()
.parallel()
.flatMap(s -> s.chars().boxed())
.filter(c -> !punctuationChars.contains((char) c.intValue()))
)
.foldLeft(
Tuple("", HashMap.<String, Integer>empty()),
(tup, integer) -> Character.isWhitespace(integer) ? tup.map(s -> "", map -> map.put(tup._1().toLowerCase(Locale.ROOT), 1, Integer::sum)) : tup.map1(s -> s + ((char) integer.intValue()))
@ -44,16 +48,26 @@ public class Analysis {
System.out.println("Cleaned tokens: " + cleaned.size());
System.out.println(cleaned);
Seq<Integer> freq = cleaned.map(Tuple2::_2);
int max = freq.max().getOrElse(0);
System.out.println("Max frequency: " + max);
int median = freq.get(freq.size() / 2);
System.out.println("Median frequency: " + median);
int cutoff = Math.max(median, 5);
int maxLength = 15;
Set<String> tokensWithPrefixes = cleaned.keySet().flatMap(Analysis::prefix);
Map<String, Integer> cut = cleaned.filterValues(i -> i > cutoff).filterKeys(s -> s.length() <= maxLength);
System.out.println(tokensWithPrefixes);
System.out.println("Common tokens: " + cut.size());
Map<String, List<String>> probabilities = tokensWithPrefixes.toMap(Function.identity(), token -> cleaned
Set<String> tokensWithPrefixes = cut.keySet().flatMap(Analysis::prefix);
System.out.println("Tokens and prefixes: " + tokensWithPrefixes.size());
Map<String, List<String>> probabilities = tokensWithPrefixes.toMap(Function.identity(), token -> cut
.filterKeys(key -> key.startsWith(token))
.map((key, value) -> Tuple(key.substring(token.length()), value))
.toList()
.flatMap(t -> prefix(t._1()).map(it -> Tuple(t._2(), it)))
.foldLeft(HashMap.<String, Integer>empty(), (run, k) -> run.put(k._2(), k._1(), Integer::sum))
.toList()
@ -66,14 +80,23 @@ public class Analysis {
._2()
.map(Tuple2::_1));
System.out.println(probabilities);
System.out.println("Generated probabilities. Saving file...");
StringBuilder data = new StringBuilder();
probabilities.forEach((key, values) -> {
values.take(4).forEach(it -> data.append(key).append(":").append(it).append('\n'));
});
IOUtils.write(data.toString(), new FileOutputStream("./corpus.8vim"), Charset.defaultCharset());
System.out.println("Saved.");
Scanner s = new Scanner(System.in);
while (true) {
System.out.print("Start: ");
String start = s.nextLine();
System.out.println(probabilities.get(start));
}
}
}

@ -0,0 +1,40 @@
package com.dfsek.corpus;
import io.vavr.collection.HashMap;
import io.vavr.collection.List;
import io.vavr.collection.Map;
import io.vavr.collection.Stream;
import org.apache.commons.io.IOUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Scanner;
import static io.vavr.API.*;
public class Read {
public static void main(String... args) throws IOException {
System.out.println("Parsing probabilities...");
String corpus = IOUtils.resourceToString("/corpus.8vim", Charset.defaultCharset());
long st = System.nanoTime();
Map<String, List<String>> probabilities = parseCorpus(corpus);
long e = System.nanoTime();
long diff = e - st;
System.out.println("Done (" + ((double) diff / 1000000) + "ms).");
Scanner s = new Scanner(System.in);
while (true) {
System.out.print("Start: ");
String start = s.nextLine();
System.out.println(probabilities.get(start));
}
}
private static Map<String, List<String>> parseCorpus(String in) {
return Stream.ofAll(new BufferedReader(new StringReader(in)).lines())
.map(s -> Tuple(s.substring(0, s.indexOf(':')), s.substring(s.indexOf(':'))))
.foldLeft(HashMap.empty(), (run, tup) -> run.put(tup._1(), List(tup._2()), List::appendAll));
}
}
Loading…
Cancel
Save