Compare commits

..

32 Commits

Author SHA1 Message Date
Jules Aguillon
44b1086be9 Release 1.13.0 (17) 2022-02-22 20:06:31 +01:00
Jules Aguillon
d277ed1a8a Remove translated changelogs
A single changelog is easier to write. The existence of translated older
changelogs is probably not a good idea either.
2022-02-22 19:52:09 +01:00
Jules Aguillon
7aa280b888 Tweak repeat timing for modulated keys
Make modulated keys repeat twice as slow by default and start repeating
twice as early.
2022-02-22 19:46:41 +01:00
Jules Aguillon
14dabb6f51 Send key events for the modifiers
Before sending a key event while modifiers are active, send events for
the modifier keys.

Some applications don't look at the "metaState" flags but instead keep
track of the up and down events for the modifiers.

For example, the basic text views that are in every applications
correctly handle the "metaState" flags except for one binding:
Selecting text with the arrows while pressing shift.
2022-02-22 19:32:16 +01:00
Jules Aguillon
84bce23fec Merge pull request #75 from gh0ste/master
spanish translation and layout
2022-02-22 19:10:10 +01:00
Jules Aguillon
583aa55259 Update the spanish locale
To use the new layout.
2022-02-22 19:08:44 +01:00
Invert White
4873b8e22a spanish translation and layout 2022-02-22 00:20:11 -03:00
Jules Aguillon
c85e9a91d1 Improve modulated key repeat
Change the formula: don't use an external constant, add a state.
It's now the ratio between where the finger is at the first repeat and
where it is now.

Keep the repeat going when swiping into an other key. Currently only for
arrows: It's now possible to go from an arrow to an other without
waiting again for the key repeat timeout.

The backspace and delete keys don't work well with this.
2022-02-21 00:24:57 +01:00
Jules Aguillon
51ff795be4 Move pointer handling code to its own class
Separate the concerns and have a clearer interface between the two parts
of the code.
2022-02-20 13:09:39 +01:00
Jules Aguillon
632a9ac590 Localize € and £
Show these characters only for users that have the corresponding locale
installed (a supported eu language for €, en-GB for £).
Add these characters to most layouts.
2022-02-19 21:48:48 +01:00
Raphael
d73be58c1a add colon key to numeric keyboard 2022-02-19 21:02:39 +01:00
Jules Aguillon
04c84a8f66 Add Ord+* = ° 2022-02-19 21:01:52 +01:00
Raphael
d2570bc2ea add-ordinal-numbers-symbol-system 2022-02-19 20:59:49 +01:00
Raphael
4b1a6e4b92 Create github action to compile debug_APK (#60)
* Create github action to make debug apk

* Cache debug signing certificate

* Setup cachix

* Run on pull request

Co-authored-by: Jules Aguillon <jules@j3s.fr>
2022-02-14 00:42:41 +01:00
Jules Aguillon
c05fdea765 Define the height of the keyboard relative to the screen size
Depending on the pixel density isn't ideal for a keyboard, which would
render differently depending on the "scaling" accessibility option.

Landscape mode needs a special values. At the same time, increase the
horizontal margin when landscape.
2022-02-13 15:58:30 +01:00
Vladimir Chernov
4e98ab7515 Add Russian layout (#66)
* Add Russian layout

Co-authored-by: Jules Aguillon <jules@j3s.fr>
2022-02-13 13:56:46 +01:00
Jules Aguillon
fda6895dc8 Use the improved font for shift, globe, enter and space
These glyph were available in the custom font but not used yet.
2022-02-13 13:46:37 +01:00
Jules Aguillon
b488c766b1 Add ю to the bulgarian layout
Thanks Zdravko for pointing out.
2022-02-13 13:23:49 +01:00
Jules Aguillon
94fed03a67 Scale the bottom row depending on the host layout
1ff8526 added a bug for layouts that weren't 10 units wide.
2022-02-13 13:20:22 +01:00
Jules Aguillon
adb77466f5 Fix forced inverted colors on Xiaomi phones
MIUI inverts the colors of the app it thinks doesn't implement dark
themes correctly. Also, it inverts the colors in the dumbest possible
way: it doesn't invert all the colors the same way.

It thinks that presumably because I don't use the Material base themes.
2022-02-13 11:45:13 +01:00
Roy-Orbison
6ab6a6811b Invert return key in TTF (#67)
Was a "level-up" symbol, now looks like a return key in identical style.
2022-02-13 11:25:59 +01:00
Jules Aguillon
b1ef21f62f Improve QWERTZ
- Add the umlauts back.
- Remove the accents. QWERTZ changes from "programming layout" to
  "localized layout".
- Move the '?' away from the edge of the screen.
2022-02-07 01:22:42 +01:00
Jules Aguillon
75c736709d build: Use d8 instead of dx
dx have been removed in android build tools >30.0.3 in favor of d8.
Lift the version constraint on the build tools.
2022-02-07 01:11:55 +01:00
Jules Aguillon
1ff8526d24 Define the bottom row separately
Avoid divergences when the bottom row is modified.
2022-02-07 00:55:32 +01:00
AlexandraAlter
a76541458d Dvorak layout (#16) 2022-02-07 00:06:49 +01:00
Max Schillinger
d014a7dd8c Replace unusual return symbol 2022-02-06 23:58:17 +01:00
Max Schillinger
93edc4ac42 Allow egde keys instead of corner keys (swipe vertically/horizontally)
Add a new boolean parameter "edgekeys" for defining keys that have the
additional (swipe) keys on the edges (top, right, left, bottom) instead
of at the corners (top left, top right, bottom left, bottom right).
2022-02-06 23:49:43 +01:00
Jules Aguillon
7383cc4b68 Add the Meta key to every layouts
To follow soon: Define the bottom row separately from layouts.
2022-02-06 23:12:24 +01:00
Jules Aguillon
95c8acc31e Add the Meta key
Currently using the diamond symbol like the history meta key: https://en.wikipedia.org/wiki/Meta_key
However, this key is actually interpreted as the Super/Windows key but
Android calls it "meta" internally.
2022-02-06 23:01:35 +01:00
Jules Aguillon
e86173b895 Use apksigner from the build tools for release builds too 2022-02-05 20:22:40 +01:00
Max Schillinger
be0f4a1fc9 Makefile: Pass full path when calling apksigner 2022-02-05 20:15:29 +01:00
Raphael
b913d6842f Add supoort to show accents for Portuguese language 2022-02-05 20:07:32 +01:00
43 changed files with 936 additions and 515 deletions

36
.github/workflows/make-apk.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Make Apk CI
on:
workflow_dispatch:
push:
pull_request:
jobs:
Build-Apk:
runs-on: ubuntu-latest
steps:
- name: Install nix
uses: cachix/install-nix-action@v15
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v10
with:
name: julow
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- name: Checkout Repo
uses: actions/checkout@v2
- name: Cache debug certificate
uses: actions/cache@v2
with:
path: _build/debug.keystore
key: debug-keystore
- name: Run nix-shell and Make
uses: ZenithalHourlyRate/nix-shell-action@v4
with:
file: shell.nix
script: make
- name: Save debug apk
uses: actions/upload-artifact@v2
with:
name: debug apk
path: _build/*.apk

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="juloo.keyboard2" android:versionCode="16" android:versionName="1.12.0" android:hardwareAccelerated="false">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="juloo.keyboard2" android:versionCode="17" android:versionName="1.13.0" android:hardwareAccelerated="false">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="30"/>
<application android:label="@string/app_name" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:hardwareAccelerated="false">
<service android:name="juloo.keyboard2.Keyboard2" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD">

View File

@@ -9,7 +9,7 @@ Android Studio.
Fortunately, there's not many dependencies:
- OpenJDK 8
- Android SDK: build tools `30.0.3`, platform `30`
- Android SDK: build tools (minimum `28.0.1`), platform `30`
- Make sure to have the `$ANDROID_HOME` environment variable set.
For Nix users, there's a `shell.nix` for setting-up the right environment.

View File

@@ -55,7 +55,7 @@ $(DEBUG_KEYSTORE):
-keyalg rsa -storepass $(DEBUG_PASSWD) -validity 10000
_build/%.debug.apk: _build/%.debug.unsigned-apk $(DEBUG_KEYSTORE)
apksigner sign --in "$<" --out "$@" \
$(ANDROID_BUILD_TOOLS)/apksigner sign --in "$<" --out "$@" \
--ks $(DEBUG_KEYSTORE) --ks-key-alias debug --ks-pass "pass:$(DEBUG_PASSWD)"
# Debug apk
@@ -68,7 +68,7 @@ _build/$(PACKAGE_NAME).debug.unsigned-apk: AAPT_PACKAGE_FLAGS+=--rename-manifest
# it is interpreted as a shell script
_build/%.apk: _build/%.unsigned-apk %-keystore.conf
eval `cat $(word 2,$^)` && \
apksigner sign --in "$<" --out "$@" \
$(ANDROID_BUILD_TOOLS)/apksigner sign --in "$<" --out "$@" \
--ks "$$KEYSTORE" --ks-key-alias "$$KEYNAME" --ks-pass "pass:$$KEYSTOREPASS"
# Package
@@ -105,4 +105,4 @@ _build/classes.dex: $(JAVA_FILES) $(R_FILE)
-classpath $(ANDROID_PLATFORM)/android.jar:$(EXTRA_JARS) \
-sourcepath $(SRC_DIR):$(GEN_DIR) \
$^
$(ANDROID_BUILD_TOOLS)/dx --dex --output="$@" $(OBJ_DIR) $(subst :, ,$(EXTRA_JARS))
$(ANDROID_BUILD_TOOLS)/d8 --output $(@D) $(OBJ_DIR)/*/*/* $(subst :, ,$(EXTRA_JARS))

Binary file not shown.

View File

@@ -0,0 +1,13 @@
New languages: Portuguese (thanks @sdrapha), Russian (thanks @carrot-cookie), Spanish (thanks @gh0ste)
New layouts: Dvorak (thanks @AlexandraAlter), Russian Jcuken, Spanish Qwerty
Added the Meta key (the equivalent of the Win on Windows)
Added ordinal symbols on the number pane (thanks @sdrapha)
Better position for the arrow keys (thanks @MaxGyver83)
Improvements to some of the symbols (thanks @MaxGyver83, @Roy-Orbison)
Improvements to the layouts
Improvements to the key-repeat for arrows
Fixed Shift+Arrows for selecting text
Fixed dark theme bugs on Xiaomi phones
Thanks to all the contributors: @sdrapha, @MaxGyver83, @AlexandraAlter, @Roy-Orbison, @carrot-cookie, @gh0ste

View File

@@ -0,0 +1 @@
Un teclado virtual ligero para desarrolladores.

View File

@@ -0,0 +1 @@
Unexpected Keyboard

View File

@@ -1,3 +0,0 @@
- Ajout de l'Espagnol et l'Italien
- Nouvel emplacement pour les accents (encore) et ajout de nouveaux caratères
- Correction de bugs (un crash sur d'anciennes versions d'Android et un rare glitch graphique après une rotation)

View File

@@ -1 +0,0 @@
Première version open-source !

View File

@@ -1,3 +0,0 @@
- Nouvelle langue: Suédois
- Les raccourcis clavier fonctionnent dans toutes les applications
- Correction d'un bug graphique et quelques ajustements

View File

@@ -1,8 +0,0 @@
Nouvelle langue : Allemand
Nouvelle configuration du clavier : QWERTZ
Thèmes : Clair, Sombre et Noir OLED
Ajout de la touche Action, à côté de la touche Entrer, nécessaire pour certaines applications.
Amélioration des options et résolution de bugs.

View File

@@ -1,8 +0,0 @@
Nouvelle langue : Allemand
Nouvelle configuration du clavier : QWERTZ
Thèmes : Clair, Sombre et Noir OLED
Ajout de la touche Action, à côté de la touche Entrer, nécessaire pour certaines applications.
Amélioration des options et résolution de bugs.

View File

@@ -1,12 +0,0 @@
Nouvelles langues: Letton (merci @eandersons), Bulgare (merci Zdravko Iskrenov)
Nouvelles dispositions: Bulgare, Letton
L'application est traduite en Français et Letton.
Amélioration de la touche Action.
La touche globe ouvre le menu de sélection des claviers.
Fn+Tab pour écrire le charactère Tab littérallement.
Nouvelles options pour contrôler l'espacement entre les touches.
Meilleure intégration avec le thème système.
Merci à tous les contributeurs: @Moini, @sdrapha, @eandersons, @Poussinou, Zdravko Iskrenov

48
res/values-es/strings.xml Normal file
View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Unexpected Keyboard</string>
<string name="settings_activity_label">Ajustes de Unexpected Keyboard</string>
<string name="pref_category_layout">Formato</string>
<string name="pref_layout_title">Cambiar formato de teclado</string>
<string name="pref_layout_e_system">Ajustes del sistema</string>
<string name="pref_accents_title">Acentos</string>
<string name="pref_accents_e_all_installed">Mostrar acentos para todos los lenguajes instalados</string>
<string name="pref_accents_e_selected">Solo mostrar acentos para el lenguaje seleccionado</string>
<string name="pref_accents_e_all">Mostrar todos los acentos</string>
<string name="pref_accents_e_none">Ocultar acentos</string>
<string name="pref_category_typing">Escribiendo</string>
<string name="pref_swipe_dist_title">Distancia para deslizar</string>
<string name="pref_swipe_dist_summary">Distancia de caractéres en las esquinas de las letras (%s)</string>
<string name="pref_long_timeout_title">Tiempo de espera de repetición de tecla</string>
<string name="pref_long_interval_title">Intervalo de repetición de tecla</string>
<string name="pref_category_vibrate">Vibración</string>
<string name="pref_vibrate_title">Vibración</string>
<string name="pref_vibrate_summary">Habilitar/deshabilitar vibración al presionar una tecla</string>
<string name="pref_vibrate_duration_title">Duración</string>
<string name="pref_precise_repeat_title">Movimientos de cursor preciso</string>
<string name="pref_precise_repeat_summary">Modular la velocidad de repetición de teclas según si se desliza más o menos</string>
<string name="pref_category_style">Estilo</string>
<string name="pref_margin_bottom_title">Margen del pie</string>
<string name="pref_keyboard_height_title">Altura del teclado</string>
<string name="pref_horizontal_margin_title">Margen horizontal</string>
<string name="pref_character_size_title">Tamaño de etiqueta</string>
<string name="pref_character_size_summary">Tamaño de caractéres mostrados en el teclado (%.2fx)</string>
<string name="pref_theme">Tema</string>
<string name="pref_theme_e_system">Ajustes de sistema</string>
<string name="pref_theme_e_dark">Oscuro</string>
<string name="pref_theme_e_light">Claro</string>
<string name="pref_theme_e_black">Negro</string>
<string name="pref_swipe_dist_e_very_short">Muy corto</string>
<string name="pref_swipe_dist_e_short">Corto</string>
<string name="pref_swipe_dist_e_default">Normal</string>
<string name="pref_swipe_dist_e_far">Lejano</string>
<string name="pref_swipe_dist_e_very_far">Muy lejano</string>
<string name="pref_key_horizontal_space">Espaciado horizontal entre las teclas</string>
<string name="pref_key_vertical_space">Espaciado vertical entre las teclas</string>
<string name="key_action_next">Siguiente</string>
<string name="key_action_done">Hecho</string>
<string name="key_action_go">Ir</string>
<string name="key_action_prev">Anterior</string>
<string name="key_action_search">Buscar</string>
<string name="key_action_send">Enviar</string>
</resources>

View File

@@ -23,7 +23,7 @@
<string name="pref_precise_repeat_summary">Modifier la vitesse de répétition en bougeant le doigt</string>
<string name="pref_category_style">Style</string>
<string name="pref_margin_bottom_title">Marge du bas</string>
<string name="pref_key_height_title">Hauteur des touches</string>
<string name="pref_keyboard_height_title">Hauteur du clavier</string>
<string name="pref_horizontal_margin_title">Marge des côtés</string>
<string name="pref_character_size_title">Taille des labels</string>
<string name="pref_character_size_summary">Taille des caractères affichés sur les touches (%.2fx)</string>

View File

@@ -23,7 +23,7 @@
<string name="pref_precise_repeat_summary">Mainīt taustiņa atkārtošanās ātrumu ar pavilkšanas attālumu</string>
<string name="pref_category_style">Izskata pielāgojumi</string>
<string name="pref_margin_bottom_title">Apakšējā apmale</string>
<string name="pref_key_height_title">Taustiņa augstums</string>
<string name="pref_keyboard_height_title">Tastatūras augstums</string>
<string name="pref_horizontal_margin_title">Līmeniskā apmale</string>
<string name="pref_character_size_title">Iezīmes izmērs</string>
<string name="pref_character_size_summary">Tastatūrā attēloto rakstzīmju izmērs (%.2fx)</string>

View File

@@ -4,17 +4,23 @@
<item>system</item>
<item>azerty</item>
<item>qwerty</item>
<item>qwerty_es</item>
<item>qwerty_lv</item>
<item>ru_jcuken</item>
<item>qwertz</item>
<item>bgph1</item>
<item>dvorak</item>
</string-array>
<string-array name="pref_layout_entries">
<item>@string/pref_layout_e_system</item>
<item>AZERTY</item>
<item>QWERTY</item>
<item>QWERTY (Español)</item>
<item>QWERTY (Latvian)</item>
<item>ЙЦУКЕН (Русский)</item>
<item>QWERTZ</item>
<item>Bulgarian (Phonetic Traditional)</item>
<item>Dvorak</item>
</string-array>
<string-array name="pref_accents_entries">
<item>@string/pref_accents_e_all_installed</item>

View File

@@ -12,4 +12,5 @@
<dimen name="emoji_type_button_height">56dp</dimen>
<dimen name="emoji_grid_height">250dp</dimen>
<dimen name="emoji_text_size">28dp</dimen>
<dimen name="landscape_extra_horizontal_margin">20dp</dimen>
</resources>

View File

@@ -23,7 +23,7 @@
<string name="pref_precise_repeat_summary">Modulate key repeat speed by swiping more or less</string>
<string name="pref_category_style">Style</string>
<string name="pref_margin_bottom_title">Margin bottom</string>
<string name="pref_key_height_title">Key height</string>
<string name="pref_keyboard_height_title">Keyboard height</string>
<string name="pref_horizontal_margin_title">Horizontal margin</string>
<string name="pref_character_size_title">Label size</string>
<string name="pref_character_size_summary">Size of characters displayed on the keyboard (%.2fx)</string>

View File

@@ -23,6 +23,7 @@
</declare-styleable>
<style name="Dark">
<item name="android:isLightTheme">false</item>
<item name="android:forceDarkAllowed">false</item>
<item name="colorKeyboard">#1B1B1B</item>
<item name="colorKey">#303030</item>
<item name="colorKeyActivated">#1B1B1B</item>
@@ -54,6 +55,7 @@
</style>
<style name="Black">
<item name="android:isLightTheme">false</item>
<item name="android:forceDarkAllowed">false</item>
<item name="colorKeyboard">#000000</item>
<item name="colorKey">#000000</item>
<item name="colorKeyActivated">#333333</item>

View File

@@ -34,11 +34,4 @@
<key key0="n" key1="accent_tilde" key2="§" key4="!"/>
<key width="2.0" key0="backspace" key2="delete"/>
</row>
<row height="0.95">
<key width="1.8" key0="ctrl" key3="switch_numeric"/>
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

8
res/xml/bottom_row.xml Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<row height="0.95">
<key width="1.8" key0="ctrl" key2="meta" key3="switch_numeric"/>
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down" edgekeys="true"/>
<key width="1.8" key0="enter" key2="action"/>
</row>

37
res/xml/dvorak.xml Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<keyboard>
<row>
<key key0="shift" width="1.5" key1="esc" key2="tab"/>
<key key0="p" key1="accent_ring" key2="." key3="&lt;"/>
<key key0="y" key1="accent_grave" key2="," key3="&gt;"/>
<key key0="f" key4="€"/>
<key key0="g" key2="\\" key3="|"/>
<key key0="c" key1="accent_trema" key2="accent_circonflexe" key3="{" key4="}"/>
<key key0="r" key3="[" key4="]"/>
<key key0="l" key2="=" key3="+" key4="£"/>
<key key0="backspace" key2="delete" width="1.5"/>
</row>
<row>
<key key0="a" key2="1" key3="!"/>
<key key0="o" key1="accent_macron" key2="2" key3="\@"/>
<key key0="e" key1="accent_caron" key2="3" key3="\#"/>
<key key0="u" key2="4" key3="$"/>
<key key0="i" key2="5" key3="%"/>
<key key0="d" key2="6" key3="^"/>
<key key0="h" key2="7" key3="&amp;"/>
<key key0="t" key2="8" key3="*"/>
<key key0="n" key2="9" key3="(" key4=")"/>
<key key0="s" key2="0" key3="ß"/>
</row>
<row>
<key key0="q" shift="0.5" key1="accent_tilde" key2="`" key3="~"/>
<key key0="j" key1="accent_aigu" key2="'" key3="&quot;"/>
<key key0="k" key2=";" key3=":"/>
<key key0="x" key1="accent_cedille"/>
<key key0="b"/>
<key key0="m" key2="/" key3="\?"/>
<key key0="w"/>
<key key0="v"/>
<key key0="z" key2="-" key3="_"/>
</row>
</keyboard>

View File

@@ -14,14 +14,14 @@
</row>
<row>
<key shift="0.5" key0="а" key1="tab" key2="`"/>
<key key0="с"/>
<key key0="с" key2="£"/>
<key key0="д" key1="accent_grave" key3="accent_aigu"/>
<key key0="ф"/>
<key key0="г" key1="accent_caron" key2="-" key3="_"/>
<key key0="х" key2="=" key3="+"/>
<key key0="й" key1="accent_trema" key2="accent_circonflexe" key4="}" key3="{"/>
<key key0="к" key3="[" key4="]"/>
<key key0="л" key2="|" key3="\\"/>
<key key0="л" key2="|" key3="\\" key4="ю"/>
</row>
<row>
<key width="1.5" key0="shift"/>
@@ -34,11 +34,4 @@
<key key0="м" key2="&quot;" key3="'"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
<row height="0.95">
<key width="1.8" key0="ctrl" key3="switch_numeric"/>
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<keyboard>
<row>
<key key0="й" key1="esc" key2="1" key3="~" key4="!"/>
<key key0="ц" key2="2" key3="\@"/>
<key key0="у" key2="3" key3="\#"/>
<key key0="к" key2="4" key3="$"/>
<key key0="е" key1="ё" key2="5" key3="%"/>
<key key0="н" key2="6" key3="^"/>
<key key0="г" key2="7" key3="&amp;"/>
<key key0="ш" key2="8" key3="*"/>
<key key0="щ" key2="9" key3="(" key4=")"/>
<key key0="з" key2="0"/>
<key key0="х" key1="{" key2="}"/>
<key key0="ъ" key1="[" key2="]" />
</row>
<row>
<key shift="0.5" key0="ф" key1="tab" key2="`"/>
<key key0="ы"/>
<key key0="в"/>
<key key0="а"/>
<key key0="п"/>
<key key0="р"/>
<key key0="о"/>
<key key0="л"/>
<key key0="д" key2="-" key3="_"/>
<key key0="ж" key2="=" key3="+"/>
<key key0="э" key2="|" key3="\\"/>
</row>
<row>
<key width="1.5" key0="shift"/>
<key key0="я"/>
<key key0="ч"/>
<key key0="с"/>
<key key0="м"/>
<key key0="и" key2="&lt;" key3="."/>
<key key0="т" key2="&gt;" key3=","/>
<key key0="ь" key2="\?" key3="/"/>
<key key0="б" key2=":" key3=";"/>
<key key0="ю" key2="&quot;" key3="'"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
</keyboard>

View File

@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="juloo.keyboard2.SettingsActivity" android:supportsSwitchingToNextInputMethod="true">
<subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwertz,extra_keys=trema|szlig"/>
<subtype android:label="%s" android:languageTag="en" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty"/>
<subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=aigu|tilde|trema"/>
<subtype android:label="%s" android:languageTag="fr" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=azerty,extra_keys=grave|aigu|circonflexe|cedille|trema"/>
<subtype android:label="%s" android:languageTag="it" android:imeSubtypeLocale="it_IT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=grave|aigu"/>
<subtype android:label="%s" android:languageTag="lv" android:imeSubtypeLocale="lv_LV" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty_lv,extra_keys=caron|cedille|macron"/>
<subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=aigu|trema|ring"/>
<subtype android:label="%s" android:languageTag="bg" android:imeSubtypeLocale="bg_BG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=bgph1"/>
<subtype android:label="%s" android:languageTag="bg" android:imeSubtypeLocale="bg_BG" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=bgph1|euro"/>
<subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwertz,extra_keys=trema|szlig|euro"/>
<subtype android:label="%s" android:languageTag="en-GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=pound|euro"/>
<subtype android:label="%s" android:languageTag="en-US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty"/>
<subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty_es,extra_keys=grave|aigu|tilde|trema|euro"/>
<subtype android:label="%s" android:languageTag="fr" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=azerty,extra_keys=grave|aigu|circonflexe|cedille|trema|euro"/>
<subtype android:label="%s" android:languageTag="it" android:imeSubtypeLocale="it_IT" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=grave|aigu|euro"/>
<subtype android:label="%s" android:languageTag="lv" android:imeSubtypeLocale="lv_LV" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty_lv,extra_keys=caron|cedille|macron|euro"/>
<subtype android:label="%s" android:languageTag="pt" android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=aigu|cedille|circonflexe|grave|tilde|euro"/>
<subtype android:label="%s" android:languageTag="ru" android:imeSubtypeLocale="ru_RU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=ru_jcuken"/>
<subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=qwerty,extra_keys=aigu|trema|ring|euro"/>
</input-method>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<keyboard>
<keyboard bottom_row="false">
<row>
<key width="0.75" key0="esc" key2="~" key4="!"/>
<key width="0.75" key0="(" key2="[" key4="{"/>
@@ -13,14 +13,14 @@
<key width="0.75" key0="tab" key1=";" key2="|" key4="\\"/>
<key width="0.75" key0=")" key2="]" key4="}"/>
<key key0="4"/>
<key key0="5" key1="up" key2="right" key3="left" key4="down"/>
<key key0="5" key1="up" key2="right" key3="left" key4="down" edgekeys="true"/>
<key key0="6"/>
<key width="0.75" key0="+" key1="Σ" key2="$"/>
<key width="0.75" key0="-" key2="^"/>
</row>
<row>
<key shift="0.35" width="1.15" key0="shift" key2="fn" key4="alt"/>
<key key0="1" key1="superscript" key3="subscript"/>
<key key0="1" key1="superscript" key2="ordinal" key3="subscript"/>
<key key0="2"/>
<key key0="3"/>
<key width="1.15" key0="backspace" key2="delete"/>
@@ -28,7 +28,7 @@
<row height="0.95">
<key width="1.5" key0="ctrl" key3="switch_text"/>
<key width="1.5" key0="0"/>
<key width="0.75" key0="." key2=","/>
<key width="0.75" key0="." key1=":" key2=","/>
<key width="0.75" key0="space" key1="&quot;" key2="'" key4="_"/>
<key width="1.5" key0="enter" key1="±" key2="action" key3="="/>
</row>

View File

@@ -3,7 +3,7 @@
<row>
<key key0="q" key1="esc" key2="1" key3="~" key4="!"/>
<key key0="w" key2="2" key3="\@"/>
<key key0="e" key2="3" key3="\#"/>
<key key0="e" key2="3" key3="\#" key4="€"/>
<key key0="r" key2="4" key3="$"/>
<key key0="t" key2="5" key3="%"/>
<key key0="y" key2="6" key3="^"/>
@@ -15,7 +15,7 @@
<row>
<key shift="0.5" key0="a" key1="tab" key2="`"/>
<key key0="s" key1="accent_ring" key3="ß"/>
<key key0="d" key1="accent_grave" key3="accent_aigu"/>
<key key0="d" key1="accent_grave" key2="£" key3="accent_aigu"/>
<key key0="f"/>
<key key0="g" key1="accent_caron" key2="-" key3="_"/>
<key key0="h" key2="=" key3="+"/>
@@ -34,11 +34,4 @@
<key key0="m" key2="&quot;" key3="'"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
<row height="0.95">
<key width="1.8" key0="ctrl" key3="switch_numeric"/>
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

38
res/xml/qwerty_es.xml Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<keyboard>
<row>
<key key0="q" key1="esc" key2="1" key3="~" key4="!"/>
<key key0="w" key2="2" key3="\@"/>
<key key0="e" key2="3" key3="\#" key4="€"/>
<key key0="r" key2="4" key3="$"/>
<key key0="t" key2="5" key3="%"/>
<key key0="y" key2="6" key3="^"/>
<key key0="u" key2="7" key3="&amp;"/>
<key key0="i" key2="8" key3="*"/>
<key key0="o" key2="9" key3="(" key4=")"/>
<key key0="p" key2="0"/>
</row>
<row>
<key key0="a" key2="`" key3="tab" key4="¡"/>
<key key0="s" key1="accent_ring" key3="ß"/>
<key key0="d" key1="accent_grave" key2="£" key3="accent_aigu"/>
<key key0="f"/>
<key key0="g" key1="accent_caron" key2="-" key3="_"/>
<key key0="h" key2="=" key3="+"/>
<key key0="j" key1="accent_trema" key2="accent_circonflexe" key4="}" key3="{"/>
<key key0="k" key3="[" key4="]"/>
<key key0="l" key2="|" key3="\\"/>
<key key0="ñ"/>
</row>
<row>
<key width="1.5" key0="shift"/>
<key key0="z"/>
<key key0="x"/>
<key key0="c" key1="accent_cedille" key2="&lt;" key3="."/>
<key key0="v" key2="&gt;" key3=","/>
<key key0="b" key2="\?" key3="/" key4="¿"/>
<key key0="n" key1="accent_tilde" key2=":" key3=";"/>
<key key0="m" key2="&quot;" key3="'"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
</keyboard>

View File

@@ -3,7 +3,7 @@
<row>
<key key0="q" key1="esc" key2="1" key3="~" key4="!"/>
<key key0="w" key2="2" key3="\@"/>
<key key0="e" key1="ē" key2="3" key3="\#" />
<key key0="e" key1="ē" key2="3" key3="\#" key4="€"/>
<key key0="r" key1="ŗ" key2="4" key3="$"/>
<key key0="t" key2="5" key3="%"/>
<key key0="y" key2="6" key3="^"/>
@@ -14,8 +14,8 @@
</row>
<row>
<key shift="0.5" key0="a" key1="ā"/>
<key key0="s" key1="š" />
<key key0="d" />
<key key0="s" key1="š" key3="ß"/>
<key key0="d" key2="£"/>
<key key0="f"/>
<key key0="g" key1="ģ"/>
<key key0="h" key2="accent_macron" key3="accent_caron" key4="accent_cedille"/>
@@ -34,11 +34,4 @@
<key key0="m" key1="'" key2="&quot;" key3="," key4="\?"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
<row height="0.95">
<key width="1.8" key0="ctrl" key3="switch_numeric" />
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config" />
<key width="4.0" key0="space" />
<key width="1.2" key0="." key1="up" key2="right" key3="left" key4="down" />
<key width="1.8" key0="enter" key2="action" />
</row>
</keyboard>

View File

@@ -7,19 +7,19 @@
<key key0="r" key2="4" key3="$"/>
<key key0="t" key2="5" key3="%"/>
<key key0="z" key2="6" key3="&amp;" key4="{"/>
<key key0="u" key2="7" key4="}"/>
<key key0="u" key2="7" key3="ü" key4="}"/>
<key key0="i" key1="(" key2="8" key4="["/>
<key key0="o" key1=")" key2="9" key4="]"/>
<key key0="p" key1="=" key2="0" key4="\?"/>
<key key0="o" key1=")" key2="9" key3="ö" key4="]"/>
<key key0="p" key1="=" key2="0" key3="\?"/>
</row>
<row>
<key shift="0.5" key0="a" key1="tab" key2="`"/>
<key shift="0.5" key0="a" key1="tab" key2="`" key3="ä"/>
<key key0="s" key3="ß"/>
<key key0="d" key1="accent_grave" key2="accent_aigu" key3="accent_ring"/>
<key key0="d" key2="/>
<key key0="f" key1="~"/>
<key key0="g" key3="-"/>
<key key0="h" key3="+"/>
<key key0="j" key1="accent_trema" key2="accent_circonflexe" key3="*"/>
<key key0="j" key3="*"/>
<key key0="k" key3="/" key4="\\"/>
<key key0="l" key1="'" key3="\#"/>
</row>
@@ -27,18 +27,11 @@
<key width="1.5" key0="shift"/>
<key key0="y" key1="&gt;" key2="|" key3="&lt;"/>
<key key0="x"/>
<key key0="c" key1="accent_cedille"/>
<key key0="c"/>
<key key0="v" />
<key key0="b" key1=";" key3=","/>
<key key0="n" key1=":" key3="." key4="accent_tilde"/>
<key key0="n" key1=":" key3="."/>
<key key0="m" key1="_" />
<key width="1.5" key0="backspace" key2="delete"/>
</row>
<row height="0.95">
<key width="1.8" key0="ctrl" key3="switch_numeric"/>
<key width="1.2" key0="alt" key1="fn" key2="change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space"/>
<key width="1.2" key1="up" key2="right" key3="left" key4="down"/>
<key width="1.8" key0="enter" key2="action"/>
</row>
</keyboard>

View File

@@ -17,7 +17,7 @@
<PreferenceCategory android:title="@string/pref_category_style">
<ListPreference android:key="theme" android:title="@string/pref_theme" android:summary="%s" android:defaultValue="system" android:entries="@array/pref_theme_entries" android:entryValues="@array/pref_theme_values"/>
<juloo.common.IntSlideBarPreference android:key="margin_bottom" android:title="@string/pref_margin_bottom_title" android:summary="%sdp" android:defaultValue="5" min="0" max="100"/>
<juloo.common.IntSlideBarPreference android:key="key_height" android:title="@string/pref_key_height_title" android:summary="%sdp" android:defaultValue="50" min="30" max="90"/>
<juloo.common.IntSlideBarPreference android:key="keyboard_height" android:title="@string/pref_keyboard_height_title" android:summary="%s%%" android:defaultValue="35" min="25" max="50"/>
<juloo.common.IntSlideBarPreference android:key="horizontal_margin" android:title="@string/pref_horizontal_margin_title" android:summary="%sdp" android:defaultValue="3" min="0" max="20"/>
<juloo.common.SlideBarPreference android:key="character_size" android:title="@string/pref_character_size_title" android:summary="@string/pref_character_size_summary" android:defaultValue="1.0" min="0.8" max="1.2"/>
<juloo.common.IntSlideBarPreference android:key="key_vertical_space" android:title="@string/pref_key_vertical_space" android:summary="%sdp" android:defaultValue="2" min="0" max="8"/>

View File

@@ -12,12 +12,8 @@ let
abiVersions = [ "armeabi-v7a" ];
};
apksigner = pkgs.apksigner.override {
inherit (jdk) jre;
inherit (android) build-tools;
};
in pkgs.mkShell {
buildInputs = [ pkgs.findutils jdk android.androidsdk apksigner ];
in
pkgs.mkShell {
buildInputs = [ pkgs.findutils jdk android.androidsdk ];
ANDROID_HOME = "${android.androidsdk}/libexec/android-sdk";
}

View File

@@ -81,6 +81,21 @@ final class Config
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
// The height of the keyboard is relative to the height of the screen. This
// is not the actual size of the keyboard, which will be bigger if the
// layout has a fifth row.
int keyboardHeightPercent;
float extra_horizontal_margin;
if (res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) // Landscape mode
{
keyboardHeightPercent = 55;
extra_horizontal_margin = res.getDimension(R.dimen.landscape_extra_horizontal_margin);
}
else
{
keyboardHeightPercent = prefs.getInt("keyboard_height", 35);
extra_horizontal_margin = 0.f;
}
layout = layoutId_of_string(prefs.getString("layout", "system"));
swipe_dist_dp = Float.valueOf(prefs.getString("swipe_dist", "15"));
swipe_dist_px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, swipe_dist_dp, dm);
@@ -91,10 +106,10 @@ final class Config
marginBottom = getDipPref(dm, prefs, "margin_bottom", marginBottom);
keyVerticalInterval = getDipPref(dm, prefs, "key_vertical_space", keyVerticalInterval);
keyHorizontalInterval = getDipPref(dm, prefs, "key_horizontal_space", keyHorizontalInterval);
// Add keyVerticalInterval to keyHeight because the space between the keys
// is removed from the keys during rendering
keyHeight = getDipPref(dm, prefs, "key_height", keyHeight) + keyVerticalInterval;
horizontalMargin = getDipPref(dm, prefs, "horizontal_margin", horizontalMargin);
// Do not substract keyVerticalInterval from keyHeight because this is done
// during rendered.
keyHeight = dm.heightPixels * keyboardHeightPercent / 100 / 4;
horizontalMargin = getDipPref(dm, prefs, "horizontal_margin", horizontalMargin) + extra_horizontal_margin;
preciseRepeat = prefs.getBoolean("precise_repeat", preciseRepeat);
characterSize = prefs.getFloat("character_size", characterSize);
accents = Integer.valueOf(prefs.getString("accents", "1"));
@@ -137,8 +152,11 @@ final class Config
case "azerty": return R.xml.azerty;
case "qwerty": return R.xml.qwerty;
case "qwerty_lv": return R.xml.qwerty_lv;
case "qwerty_es": return R.xml.qwerty_es;
case "ru_jcuken": return R.xml.local_ru_jcuken;
case "qwertz": return R.xml.qwertz;
case "bgph1": return R.xml.local_bgph1;
case "dvorak": return R.xml.dvorak;
case "system": default: return -1;
}
}
@@ -156,6 +174,8 @@ final class Config
case "macron": return KeyValue.FLAG_ACCENT_MACRON;
case "ring": return KeyValue.FLAG_ACCENT_RING;
case "szlig": return KeyValue.FLAG_LANG_SZLIG;
case "euro": return KeyValue.FLAG_LANG_EURO;
case "pound": return KeyValue.FLAG_LANG_POUND;
case "tilde": return KeyValue.FLAG_ACCENT4;
case "trema": return KeyValue.FLAG_ACCENT6;
default: throw new RuntimeException(name);

View File

@@ -24,12 +24,12 @@ class KeyEventHandler implements Config.IKeyEventHandler
case KeyValue.EVENT_CHANGE_METHOD: _recv.switchToNextInputMethod(); return;
case KeyValue.EVENT_ACTION: _recv.performAction(); return;
default:
if ((flags & (KeyValue.FLAG_CTRL | KeyValue.FLAG_ALT)) != 0)
handleMetaKeyUp(key, flags);
if ((flags & (KeyValue.FLAG_CTRL | KeyValue.FLAG_ALT | KeyValue.FLAG_META)) != 0)
handleKeyUpWithModifier(key, flags);
else if (key.char_ != KeyValue.CHAR_NONE)
_recv.commitChar(key.char_);
else if (key.eventCode != KeyValue.EVENT_NONE)
handleMetaKeyUp(key, flags);
handleKeyUpWithModifier(key, flags);
else
_recv.commitText(key.symbol);
}
@@ -45,18 +45,41 @@ class KeyEventHandler implements Config.IKeyEventHandler
// getCurrentInputConnection().deleteSurroundingText(before, after);
// }
private void handleMetaKeyUp(KeyValue key, int flags)
private int sendMetaKey(int eventCode, int metaFlags, int metaState, boolean down)
{
int action;
int updatedMetaState;
if (down) { action = KeyEvent.ACTION_DOWN; updatedMetaState = metaState | metaFlags; }
else { action = KeyEvent.ACTION_UP; updatedMetaState = metaState & ~metaFlags; }
_recv.sendKeyEvent(action, eventCode, metaState);
return updatedMetaState;
}
/* Send key events corresponding to pressed modifier keys. */
private int sendMetaKeys(int flags, int metaState, boolean down)
{
if ((flags & KeyValue.FLAG_CTRL) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_ON, metaState, down);
if ((flags & KeyValue.FLAG_ALT) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON, metaState, down);
if ((flags & KeyValue.FLAG_SHIFT) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON, metaState, down);
if ((flags & KeyValue.FLAG_META) != 0)
metaState = sendMetaKey(KeyEvent.KEYCODE_META_LEFT, KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON, metaState, down);
return metaState;
}
/*
* Don't set KeyEvent.FLAG_SOFT_KEYBOARD.
*/
private void handleKeyUpWithModifier(KeyValue key, int flags)
{
int meta = 0;
if (key.eventCode == KeyValue.EVENT_NONE)
return ;
if ((flags & KeyValue.FLAG_CTRL) != 0)
meta |= KeyEvent.META_CTRL_LEFT_ON | KeyEvent.META_CTRL_ON;
if ((flags & KeyValue.FLAG_ALT) != 0)
meta |= KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_ON;
if ((flags & KeyValue.FLAG_SHIFT) != 0)
meta |= KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON;
_recv.sendKeyEvent(key.eventCode, meta);
int metaState = sendMetaKeys(flags, 0, true);
_recv.sendKeyEvent(KeyEvent.ACTION_DOWN, key.eventCode, metaState);
_recv.sendKeyEvent(KeyEvent.ACTION_UP, key.eventCode, metaState);
sendMetaKeys(flags, metaState, false);
}
public static interface IReceiver
@@ -70,7 +93,7 @@ class KeyEventHandler implements Config.IKeyEventHandler
/** 'res_id' is '-1' for the currently selected layout. */
public void setLayout(int res_id);
public void sendKeyEvent(int eventCode, int meta);
public void sendKeyEvent(int eventAction, int eventCode, int meta);
public void commitText(String text);
public void commitChar(char c);

View File

@@ -113,6 +113,23 @@ class KeyModifier
{
default: return (char)KeyCharacterMap.getDeadChar('\u00AF', c);
}
case KeyValue.FLAG_ACCENT_ORDINAL:
switch (c)
{
case 'a': return 'ª';
case 'o': return 'º';
case '1': return 'ª';
case '2': return 'º';
case '3': return 'ⁿ';
case '4': return 'ᵈ';
case '5': return 'ᵉ';
case '6': return 'ʳ';
case '7': return 'ˢ';
case '8': return 'ᵗ';
case '9': return 'ʰ';
case '*': return '°';
default: return c;
}
case KeyValue.FLAG_ACCENT_SUPERSCRIPT:
switch (c)
{

View File

@@ -17,7 +17,7 @@ class KeyValue
public static final char CHAR_NONE = '\0';
// Behavior flags
public static final int FLAG_KEEP_ON = 1;
public static final int FLAG_LATCH = 1;
public static final int FLAG_LOCK = (1 << 1);
public static final int FLAG_NOREPEAT = (1 << 2);
public static final int FLAG_NOCHAR = (1 << 3);
@@ -32,6 +32,7 @@ class KeyValue
public static final int FLAG_SHIFT = (1 << 11);
public static final int FLAG_ALT = (1 << 12);
public static final int FLAG_FN = (1 << 13);
public static final int FLAG_META = (1 << 14);
// Accent flags
public static final int FLAG_ACCENT1 = (1 << 16); // Grave
@@ -45,19 +46,24 @@ class KeyValue
public static final int FLAG_ACCENT_RING = (1 << 24);
public static final int FLAG_ACCENT_CARON = (1 << 26);
public static final int FLAG_ACCENT_MACRON = (1 << 27);
public static final int FLAG_ACCENT_ORDINAL = (1 << 28);
public static final int FLAGS_ACCENTS = FLAG_ACCENT1 | FLAG_ACCENT2 |
FLAG_ACCENT3 | FLAG_ACCENT4 | FLAG_ACCENT5 | FLAG_ACCENT6 |
FLAG_ACCENT_CARON | FLAG_ACCENT_MACRON | FLAG_ACCENT_SUPERSCRIPT |
FLAG_ACCENT_SUBSCRIPT | FLAG_ACCENT_RING;
FLAG_ACCENT_SUBSCRIPT | FLAG_ACCENT_ORDINAL | FLAG_ACCENT_RING;
// Language specific keys
public static final int FLAG_LANG_SZLIG = (1 << 25);
public static final int FLAG_LANG_EURO = (1 << 29);
public static final int FLAG_LANG_POUND = (1 << 30);
public static final int FLAGS_LANGS = FLAG_LANG_SZLIG;
public static final int FLAGS_LANGS = FLAG_LANG_SZLIG | FLAG_LANG_EURO |
FLAG_LANG_POUND;
public static final int FLAGS_NOT_HIDDEN_ACCENTS = FLAG_ACCENT_SUPERSCRIPT |
FLAG_ACCENT_SUBSCRIPT;
FLAG_ACCENT_SUBSCRIPT | FLAG_ACCENT_ORDINAL;
// Keys that have to be enabled per language
public static final int FLAGS_HIDDEN_KEYS =
(FLAGS_ACCENTS & ~FLAGS_NOT_HIDDEN_ACCENTS) | FLAGS_LANGS;
@@ -120,12 +126,17 @@ class KeyValue
private static void addModifierKey(String name, String symbol, int extra_flags)
{
addKey(name, symbol, CHAR_NONE, EVENT_NONE,
FLAG_KEEP_ON | FLAG_NOCHAR | FLAG_NOREPEAT | extra_flags);
FLAG_LATCH | FLAG_NOCHAR | FLAG_NOREPEAT | extra_flags);
}
private static void addSpecialKey(String name, String symbol, int event)
{
addKey(name, symbol, CHAR_NONE, event, FLAG_NOREPEAT);
addSpecialKey(name, symbol, event, 0);
}
private static void addSpecialKey(String name, String symbol, int event, int flags)
{
addKey(name, symbol, CHAR_NONE, event, flags | FLAG_NOREPEAT);
}
private static void addEventKey(String name, String symbol, int event)
@@ -140,11 +151,7 @@ class KeyValue
static
{
String chars = "<>&\"_°~{|^}$*:!£%µ?.§€";
for (int i = 0; i < chars.length(); i++)
addCharKey(chars.charAt(i), EVENT_NONE);
addModifierKey("shift", "", FLAG_LOCK | FLAG_SHIFT);
addModifierKey("shift", "\uE808", FLAG_LOCK | FLAG_SHIFT | FLAG_KEY_FONT);
addModifierKey("ctrl", "Ctrl", FLAG_CTRL);
addModifierKey("alt", "Alt", FLAG_ALT);
addModifierKey("accent_aigu", "◌́", FLAG_ACCENT2);
@@ -158,7 +165,9 @@ class KeyValue
addModifierKey("accent_ring", "◌̊", FLAG_ACCENT_RING);
addModifierKey("superscript", "◌͆", FLAG_ACCENT_SUPERSCRIPT);
addModifierKey("subscript", "◌̺", FLAG_ACCENT_SUBSCRIPT);
addModifierKey("ordinal", "ºʳᵈ", FLAG_ACCENT_ORDINAL);
addModifierKey("fn", "Fn", FLAG_FN);
addModifierKey("meta", "", FLAG_META);
addCharKey('a', KeyEvent.KEYCODE_A);
addCharKey('b', KeyEvent.KEYCODE_B);
@@ -214,17 +223,20 @@ class KeyValue
addCharKey('(', KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN);
addCharKey(')', KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN);
addCharKey('ß', EVENT_NONE, FLAG_LANG_SZLIG);
addCharKey('€', EVENT_NONE, FLAG_LANG_EURO);
addCharKey('£', EVENT_NONE, FLAG_LANG_POUND);
addSpecialKey("config", "", EVENT_CONFIG);
addSpecialKey("switch_text", "ABC", EVENT_SWITCH_TEXT);
addSpecialKey("switch_numeric", "123+", EVENT_SWITCH_NUMERIC);
addSpecialKey("switch_emoji", "", EVENT_SWITCH_EMOJI);
addSpecialKey("switch_back_emoji", "ABC", EVENT_SWITCH_BACK_EMOJI);
addSpecialKey("change_method", "", EVENT_CHANGE_METHOD);
addSpecialKey("change_method", "\ue807", EVENT_CHANGE_METHOD, FLAG_KEY_FONT);
addSpecialKey("action", "Action", EVENT_ACTION); // Will always be replaced
addEventKey("esc", "Esc", KeyEvent.KEYCODE_ESCAPE);
addEventKey("enter", "\uE800", KeyEvent.KEYCODE_ENTER, FLAG_KEY_FONT);
// Enter should be '\u23CE' but using what is in the font file at the moment
addEventKey("enter", "\ue800", KeyEvent.KEYCODE_ENTER, FLAG_KEY_FONT);
addEventKey("up", "\uE80B", KeyEvent.KEYCODE_DPAD_UP, FLAG_KEY_FONT | FLAG_PRECISE_REPEAT);
addEventKey("right", "\uE80C", KeyEvent.KEYCODE_DPAD_RIGHT, FLAG_KEY_FONT | FLAG_PRECISE_REPEAT);
addEventKey("down", "\uE809", KeyEvent.KEYCODE_DPAD_DOWN, FLAG_KEY_FONT | FLAG_PRECISE_REPEAT);
@@ -233,8 +245,8 @@ class KeyValue
addEventKey("page_down", "", KeyEvent.KEYCODE_PAGE_DOWN);
addEventKey("home", "", KeyEvent.KEYCODE_MOVE_HOME);
addEventKey("end", "", KeyEvent.KEYCODE_MOVE_END);
addEventKey("backspace", "", KeyEvent.KEYCODE_DEL, FLAG_PRECISE_REPEAT);
addEventKey("delete", "", KeyEvent.KEYCODE_FORWARD_DEL, FLAG_PRECISE_REPEAT);
addEventKey("backspace", "", KeyEvent.KEYCODE_DEL);
addEventKey("delete", "", KeyEvent.KEYCODE_FORWARD_DEL);
addEventKey("insert", "Ins", KeyEvent.KEYCODE_INSERT);
addEventKey("f1", "F1", KeyEvent.KEYCODE_F1);
addEventKey("f2", "F2", KeyEvent.KEYCODE_F2);
@@ -249,6 +261,6 @@ class KeyValue
addEventKey("tab", "", KeyEvent.KEYCODE_TAB);
addKey("\\t", "\\t", '\t', EVENT_NONE, 0); // Send the tab character
addKey("space", " ", ' ', KeyEvent.KEYCODE_SPACE, 0);
addKey("space", "\ue80d", ' ', KeyEvent.KEYCODE_SPACE, FLAG_KEY_FONT);
}
}

View File

@@ -1,31 +1,24 @@
package juloo.keyboard2;
import android.content.Context;
import android.content.res.Configuration;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.inputmethodservice.InputMethodService;
import android.os.Build.VERSION;
import android.os.Bundle;
import android.os.IBinder;
import android.text.InputType;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.view.ContextThemeWrapper;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.util.Log;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class Keyboard2 extends InputMethodService
implements SharedPreferences.OnSharedPreferenceChangeListener
@@ -36,17 +29,9 @@ public class Keyboard2 extends InputMethodService
private Config _config;
private Map<Integer, KeyboardData> _layoutCache = new HashMap<Integer, KeyboardData>();
private KeyboardData getLayout(int resId)
{
KeyboardData l = _layoutCache.get(resId);
if (l == null)
{
l = KeyboardData.parse(getResources().getXml(resId));
_layoutCache.put(resId, l);
}
return l;
return KeyboardData.load(getResources(), resId);
}
@Override
@@ -79,6 +64,8 @@ public class Keyboard2 extends InputMethodService
String s = subtype.getExtraValueOf("default_layout");
if (s != null)
l = Config.layoutId_of_string(s);
else
l = R.xml.qwerty;
}
_currentTextLayout = l;
}
@@ -269,14 +256,12 @@ public class Keyboard2 extends InputMethodService
_keyboardView.setKeyboard(getLayout(res_id));
}
public void sendKeyEvent(int eventCode, int meta)
public void sendKeyEvent(int eventAction, int eventCode, int meta)
{
InputConnection conn = getCurrentInputConnection();
if (conn == null)
return;
KeyEvent event = new KeyEvent(1, 1, KeyEvent.ACTION_DOWN, eventCode, 0, meta);
conn.sendKeyEvent(event);
conn.sendKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
conn.sendKeyEvent(new KeyEvent(1, 1, eventAction, eventCode, 0, meta));
}
public void showKeyboardConfig()

View File

@@ -4,32 +4,27 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.PopupWindow;
import java.util.ArrayList;
public class Keyboard2View extends View
implements View.OnTouchListener, Handler.Callback
implements View.OnTouchListener, Pointers.IPointerEventHandler
{
private static final long VIBRATE_MIN_INTERVAL = 100;
private KeyboardData _keyboard;
private ArrayList<KeyDown> _downKeys = new ArrayList<KeyDown>();
private Pointers _pointers;
private int _flags = 0;
private Vibrator _vibratorService;
private long _lastVibration = 0;
private Handler _handler;
private static int _currentWhat = 0;
private Config _config;
@@ -40,13 +35,20 @@ public class Keyboard2View extends View
private static RectF _tmpRect = new RectF();
enum Vertical
{
TOP,
CENTER,
BOTTOM
}
public Keyboard2View(Context context, AttributeSet attrs)
{
super(context, attrs);
_vibratorService = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
_handler = new Handler(this);
_theme = new Theme(getContext(), attrs);
_config = Config.globalConfig();
_pointers = new Pointers(this, _config);
setOnTouchListener(this);
reset();
}
@@ -81,33 +83,74 @@ public class Keyboard2View extends View
public void reset()
{
_flags = 0;
_downKeys.clear();
_pointers.clear();
requestLayout();
invalidate();
}
public void onPointerDown(KeyValue k)
{
updateFlags();
invalidate();
if (k != null)
vibrate();
}
public void onPointerSwipe(KeyValue k)
{
updateFlags();
invalidate();
if (k != null)
vibrate();
}
public void onPointerUp(KeyValue k)
{
if (k != null && (k.flags & KeyValue.FLAG_NOCHAR) == 0)
_config.handler.handleKeyUp(k, _flags);
updateFlags();
invalidate();
}
public void onPointerHold(KeyValue k)
{
if (k != null)
_config.handler.handleKeyUp(k, _flags);
}
public void onPointerFlagsChanged()
{
updateFlags();
invalidate();
}
private void updateFlags()
{
_flags = _pointers.getFlags();
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
float x;
float y;
float keyW;
int p;
switch (event.getActionMasked())
{
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
onTouchUp(event.getPointerId(event.getActionIndex()));
_pointers.onTouchUp(event.getPointerId(event.getActionIndex()));
break ;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
p = event.getActionIndex();
onTouchDown(event.getX(p), event.getY(p), event.getPointerId(p));
float tx = event.getX(p);
float ty = event.getY(p);
KeyboardData.Key key = getKeyAtPosition(tx, ty);
if (key != null)
_pointers.onTouchDown(tx, ty, event.getPointerId(p), key);
break ;
case MotionEvent.ACTION_MOVE:
for (p = 0; p < event.getPointerCount(); p++)
onTouchMove(event.getX(p), event.getY(p), event.getPointerId(p));
_pointers.onTouchMove(event.getX(p), event.getY(p), event.getPointerId(p));
break ;
default:
return (false);
@@ -115,183 +158,33 @@ public class Keyboard2View extends View
return (true);
}
private KeyDown getKeyDown(int pointerId)
private KeyboardData.Row getRowAtPosition(float ty)
{
for (KeyDown k : _downKeys)
{
if (k.pointerId == pointerId)
return (k);
}
return (null);
}
private KeyDown getKeyDown(KeyboardData.Key key)
{
for (KeyDown k : _downKeys)
{
if (k.key == key)
return (k);
}
return (null);
}
private KeyDown getKeyDown(KeyValue kv)
{
for (KeyDown k : _downKeys)
{
if (k.value == kv)
return (k);
}
return (null);
}
private void onTouchMove(float moveX, float moveY, int pointerId)
{
KeyDown key = getKeyDown(pointerId);
KeyValue newValue;
if (key != null)
{
moveX -= key.downX;
moveY -= key.downY;
float absDist = Math.abs(moveX) + Math.abs(moveY);
key.ptrDist = absDist;
if (absDist < _config.swipe_dist_px)
newValue = key.key.key0;
else if (moveX < 0)
newValue = (moveY < 0) ? key.key.key1 : key.key.key3;
else if (moveY < 0)
newValue = key.key.key2;
else
newValue = key.key.key4;
if (newValue != null && newValue != key.value)
{
if (key.timeoutWhat != -1)
{
_handler.removeMessages(key.timeoutWhat);
if ((newValue.flags & KeyValue.FLAG_NOREPEAT) == 0)
_handler.sendEmptyMessageDelayed(key.timeoutWhat, _config.longPressTimeout);
}
key.value = newValue;
key.flags = newValue.flags;
updateFlags();
invalidate();
handleKeyDown(newValue);
}
}
}
private void onTouchDown(float touchX, float touchY, int pointerId)
{
float y = _config.marginTop - _config.keyHeight;
float y = _config.marginTop;
if (ty < y)
return null;
for (KeyboardData.Row row : _keyboard.rows)
{
y += _config.keyHeight;
if (touchY < y || touchY >= (y + _config.keyHeight))
continue ;
y += (row.shift + row.height) * _config.keyHeight;
if (ty < y)
return row;
}
return null;
}
private KeyboardData.Key getKeyAtPosition(float tx, float ty)
{
KeyboardData.Row row = getRowAtPosition(ty);
float x = _config.horizontalMargin;
if (row == null || tx < x)
return null;
for (KeyboardData.Key key : row.keys)
{
x += key.shift * _keyWidth;
float keyW = _keyWidth * key.width;
if (touchX >= x && touchX < (x + keyW))
{
int what = _currentWhat++;
if (key.key0 != null && (key.key0.flags & KeyValue.FLAG_NOREPEAT) == 0)
_handler.sendEmptyMessageDelayed(what, _config.longPressTimeout);
_downKeys.add(new KeyDown(pointerId, key, touchX, touchY, what));
handleKeyDown(key.key0);
updateFlags();
invalidate();
return ;
x += (key.shift + key.width) * _keyWidth;
if (tx < x)
return key;
}
x += keyW;
}
}
}
// Whether a key is already activated (key down but pointer up)
private KeyDown getActivatedKey(KeyValue kv)
{
for (KeyDown k : _downKeys)
{
if (k.value == kv && k.pointerId == -1)
return (k);
}
return (null);
}
private void onTouchUp(int pointerId)
{
KeyDown k = getKeyDown(pointerId);
if (k != null)
{
// Stop key repeat
if (k.timeoutWhat != -1)
{
_handler.removeMessages(k.timeoutWhat);
k.timeoutWhat = -1;
}
KeyDown k_on = getActivatedKey(k.value);
if (k_on != null)
{
_downKeys.remove(k); // Remove dupplicate
// Same key with FLAG_LOCK is already on, do lock
if ((k_on.flags & KeyValue.FLAG_LOCK) != 0)
{
k_on.flags ^= KeyValue.FLAG_LOCK; // Next time, disable it
k_on.flags |= KeyValue.FLAG_LOCKED;
}
// Otherwise, toggle it
else
{
_downKeys.remove(k_on);
}
}
// Key stay activated
else if ((k.flags & KeyValue.FLAG_KEEP_ON) != 0)
{
k.pointerId = -1; // Set pointer up
}
else // Regular key up
{
for (int i = 0; i < _downKeys.size(); i++)
{
KeyDown downKey = _downKeys.get(i);
// Disable other activated keys that aren't locked
if (downKey.pointerId == -1 && (downKey.flags & KeyValue.FLAG_LOCKED) == 0)
_downKeys.remove(i--);
// Other keys currently down won't stay activated
else if ((downKey.flags & KeyValue.FLAG_KEEP_ON) != 0)
downKey.flags ^= KeyValue.FLAG_KEEP_ON;
}
_downKeys.remove(k);
handleKeyUp(k);
}
updateFlags();
invalidate();
}
}
private void handleKeyUp(KeyDown key)
{
if (key.value != null && (key.flags & (KeyValue.FLAG_LOCKED | KeyValue.FLAG_NOCHAR)) == 0)
_config.handler.handleKeyUp(key.value, _flags);
}
private void handleKeyDown(KeyValue key)
{
if (key == null)
return ;
vibrate();
}
private void updateFlags()
{
_flags = 0;
for (KeyDown k : _downKeys)
_flags |= k.flags;
return null;
}
private void vibrate()
@@ -313,28 +206,6 @@ public class Keyboard2View extends View
}
}
@Override
public boolean handleMessage(Message msg)
{
for (KeyDown key : _downKeys)
{
if (key.timeoutWhat == msg.what)
{
long nextInterval = _config.longPressInterval;
if (_config.preciseRepeat && (key.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
{
// Modulate repeat interval depending on the distance of the pointer
float accel = Math.min(4.f, Math.max(0.3f, key.ptrDist / (_config.swipe_dist_px * 15.f)));
nextInterval = (long)((float)nextInterval / accel);
}
_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
_config.handler.handleKeyUp(key.value, _flags);
return (true);
}
}
return (false);
}
@Override
public void onMeasure(int wSpec, int hSpec)
{
@@ -361,21 +232,35 @@ public class Keyboard2View extends View
{
x += k.shift * _keyWidth;
float keyW = _keyWidth * k.width - _config.keyHorizontalInterval;
KeyDown keyDown = getKeyDown(k);
boolean isKeyDown = _pointers.isKeyDown(k);
_tmpRect.set(x, y, x + keyW, y + keyH);
canvas.drawRoundRect(_tmpRect, _theme.keyBorderRadius, _theme.keyBorderRadius,
(keyDown != null) ? _theme.keyDownBgPaint : _theme.keyBgPaint);
isKeyDown ? _theme.keyDownBgPaint : _theme.keyBgPaint);
if (k.key0 != null)
drawLabel(canvas, k.key0, keyW / 2f + x, (keyH + _theme.labelTextSize) / 2f + y, keyDown);
drawLabel(canvas, k.key0, keyW / 2f + x, (keyH + _theme.labelTextSize) / 2f + y, isKeyDown);
float subPadding = _config.keyPadding;
if (k.key1 != null)
drawSubLabel(canvas, k.key1, x + subPadding, y + subPadding, false, true, keyDown);
if (k.key3 != null)
drawSubLabel(canvas, k.key3, x + subPadding, y + keyH - subPadding, false, false, keyDown);
if (k.key2 != null)
drawSubLabel(canvas, k.key2, x + keyW - subPadding, y + subPadding, true, true, keyDown);
if (k.key4 != null)
drawSubLabel(canvas, k.key4, x + keyW - subPadding, y + keyH - subPadding, true, false, keyDown);
if (k.edgekeys)
{
if (k.key1 != null) // top key
drawSubLabel(canvas, k.key1, x + keyW / 2f, y + subPadding, Paint.Align.CENTER, Vertical.TOP, isKeyDown);
if (k.key3 != null) // left key
drawSubLabel(canvas, k.key3, x + subPadding, y + keyH / 2f, Paint.Align.LEFT, Vertical.CENTER, isKeyDown);
if (k.key2 != null) // right key
drawSubLabel(canvas, k.key2, x + keyW - subPadding, y + keyH / 2f, Paint.Align.RIGHT, Vertical.CENTER, isKeyDown);
if (k.key4 != null) // bottom key
drawSubLabel(canvas, k.key4, x + keyW / 2f, y + keyH - subPadding, Paint.Align.CENTER, Vertical.BOTTOM, isKeyDown);
}
else
{
if (k.key1 != null) // top left key
drawSubLabel(canvas, k.key1, x + subPadding, y + subPadding, Paint.Align.LEFT, Vertical.TOP, isKeyDown);
if (k.key3 != null) // bottom left key
drawSubLabel(canvas, k.key3, x + subPadding, y + keyH - subPadding, Paint.Align.LEFT, Vertical.BOTTOM, isKeyDown);
if (k.key2 != null) // top right key
drawSubLabel(canvas, k.key2, x + keyW - subPadding, y + subPadding, Paint.Align.RIGHT, Vertical.TOP, isKeyDown);
if (k.key4 != null) // bottom right key
drawSubLabel(canvas, k.key4, x + keyW - subPadding, y + keyH - subPadding, Paint.Align.RIGHT, Vertical.BOTTOM, isKeyDown);
}
x += _keyWidth * k.width;
}
y += row.height * _config.keyHeight;
@@ -388,38 +273,41 @@ public class Keyboard2View extends View
super.onDetachedFromWindow();
}
private int labelColor(KeyValue k, KeyDown hasKeyDown, int defaultColor)
private int labelColor(KeyValue k, boolean isKeyDown, int defaultColor)
{
if (hasKeyDown != null)
if (isKeyDown && (k.flags & KeyValue.FLAG_LATCH) != 0)
{
KeyDown kd = getKeyDown(k);
if (kd != null)
int flags = _pointers.getKeyFlags(k);
if (flags != -1)
{
if ((kd.flags & KeyValue.FLAG_LOCKED) != 0)
if ((flags & KeyValue.FLAG_LOCKED) != 0)
return _theme.lockedColor;
if (kd.pointerId == -1)
if ((flags & KeyValue.FLAG_LATCH) == 0)
return _theme.activatedColor;
}
}
return defaultColor;
}
private void drawLabel(Canvas canvas, KeyValue k, float x, float y, KeyDown keyDown)
private void drawLabel(Canvas canvas, KeyValue k, float x, float y, boolean isKeyDown)
{
k = KeyModifier.handleFlags(k, _flags);
Paint p = _theme.labelPaint(((k.flags & KeyValue.FLAG_KEY_FONT) != 0));
p.setColor(labelColor(k, keyDown, _theme.labelColor));
p.setColor(labelColor(k, isKeyDown, _theme.labelColor));
p.setTextSize(_theme.labelTextSize * scaleTextSize(k));
canvas.drawText(k.symbol, x, y, p);
}
private void drawSubLabel(Canvas canvas, KeyValue k, float x, float y, boolean right, boolean up, KeyDown keyDown)
private void drawSubLabel(Canvas canvas, KeyValue k, float x, float y, Paint.Align a, Vertical v, boolean isKeyDown)
{
k = KeyModifier.handleFlags(k, _flags);
Paint p = _theme.subLabelPaint(((k.flags & KeyValue.FLAG_KEY_FONT) != 0), right);
p.setColor(labelColor(k, keyDown, _theme.subLabelColor));
Paint p = _theme.subLabelPaint(((k.flags & KeyValue.FLAG_KEY_FONT) != 0), a);
p.setColor(labelColor(k, isKeyDown, _theme.subLabelColor));
p.setTextSize(_theme.sublabelTextSize * scaleTextSize(k));
y -= up ? p.ascent() : p.descent();
if (v == Vertical.CENTER)
y -= (p.ascent() + p.descent()) / 2f;
else
y -= (v == Vertical.TOP) ? p.ascent() : p.descent();
canvas.drawText(k.symbol, x, y, p);
}
@@ -427,30 +315,4 @@ public class Keyboard2View extends View
{
return ((k.symbol.length() < 2) ? 1.f : 0.8f) * _config.characterSize;
}
private static class KeyDown
{
/* -1 if pointer is up. */
public int pointerId;
public KeyValue value;
public KeyboardData.Key key;
public float downX;
public float downY;
/* Manhattan distance of the pointer to the center of the key */
public float ptrDist;
public int flags;
public int timeoutWhat;
public KeyDown(int pointerId, KeyboardData.Key key, float x, float y, int what)
{
this.pointerId = pointerId;
value = key.key0;
this.key = key;
downX = x;
downY = y;
ptrDist = 0.f;
flags = (value == null) ? 0 : value.flags;
timeoutWhat = what;
}
}
}

View File

@@ -1,8 +1,11 @@
package juloo.keyboard2;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class KeyboardData
{
@@ -12,70 +15,87 @@ class KeyboardData
/* Total height of the keyboard. Unit is abstract. */
public final float keysHeight;
public KeyboardData(List<Row> rows_)
{
float kw = 0.f;
float kh = 0.f;
for (Row r : rows_)
{
kw = Math.max(kw, r.keysWidth);
kh += r.height + r.shift;
}
rows = rows_;
keysWidth = kw;
keysHeight = kh;
}
public static KeyboardData parse(XmlResourceParser parser)
{
ArrayList<Row> rows = new ArrayList<Row>();
try
{
int status;
while (parser.next() != XmlResourceParser.START_TAG)
continue ;
if (!parser.getName().equals("keyboard"))
throw new Exception("Unknow tag: " + parser.getName());
while ((status = parser.next()) != XmlResourceParser.END_DOCUMENT)
{
if (status == XmlResourceParser.START_TAG)
{
String tag = parser.getName();
if (tag.equals("row"))
rows.add(Row.parse(parser));
else
throw new Exception("Unknow keyboard tag: " + tag);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return new KeyboardData(rows);
}
public KeyboardData replaceKeys(MapKeys f)
{
ArrayList<Row> rows_ = new ArrayList<Row>();
for (Row r : rows)
rows_.add(r.replaceKeys(f));
return new KeyboardData(rows_);
return new KeyboardData(rows_, keysWidth);
}
private static Row _bottomRow = null;
private static Map<Integer, KeyboardData> _layoutCache = new HashMap<Integer, KeyboardData>();
public static KeyboardData load(Resources res, int id)
{
KeyboardData l = _layoutCache.get(id);
if (l == null)
{
try
{
if (_bottomRow == null)
_bottomRow = parse_bottom_row(res.getXml(R.xml.bottom_row));
l = parse_keyboard(res.getXml(id));
_layoutCache.put(id, l);
}
catch (Exception e)
{
e.printStackTrace();
}
}
return l;
}
private static KeyboardData parse_keyboard(XmlResourceParser parser) throws Exception
{
if (!expect_tag(parser, "keyboard"))
throw new Exception("Empty layout file");
boolean bottom_row = parser.getAttributeBooleanValue(null, "bottom_row", true);
ArrayList<Row> rows = new ArrayList<Row>();
while (expect_tag(parser, "row"))
rows.add(Row.parse(parser));
float kw = compute_max_width(rows);
if (bottom_row)
rows.add(_bottomRow.updateWidth(kw));
return new KeyboardData(rows, kw);
}
private static float compute_max_width(List<Row> rows)
{
float w = 0.f;
for (Row r : rows)
w = Math.max(w, r.keysWidth);
return w;
}
private static Row parse_bottom_row(XmlResourceParser parser) throws Exception
{
if (!expect_tag(parser, "row"))
throw new Exception("Failed to parse bottom row");
return Row.parse(parser);
}
protected KeyboardData(List<Row> rows_, float kw)
{
float kh = 0.f;
for (Row r : rows_)
kh += r.height + r.shift;
rows = rows_;
keysWidth = kw;
keysHeight = kh;
}
public static class Row
{
public final List<Key> keys;
/* Height of the row. Unit is abstract. */
/* Height of the row, without 'shift'. Unit is abstract. */
public final float height;
/* Extra empty space on the top. */
public final float shift;
/* Total width of very keys. Unit is abstract. */
/* Total width of the row. Unit is abstract. */
private final float keysWidth;
public Row(List<Key> keys_, float h, float s)
protected Row(List<Key> keys_, float h, float s)
{
float kw = 0.f;
for (Key k : keys_) kw += k.width + k.shift;
@@ -91,17 +111,8 @@ class KeyboardData
int status;
float h = parser.getAttributeFloatValue(null, "height", 1f);
float shift = parser.getAttributeFloatValue(null, "shift", 0f);
while ((status = parser.next()) != XmlResourceParser.END_TAG)
{
if (status == XmlResourceParser.START_TAG)
{
String tag = parser.getName();
if (tag.equals("key"))
while (expect_tag(parser, "key"))
keys.add(Key.parse(parser));
else
throw new Exception("Unknow row tag: " + tag);
}
}
return new Row(keys, h, shift);
}
@@ -112,6 +123,16 @@ class KeyboardData
keys_.add(k.replaceKeys(f));
return new Row(keys_, height, shift);
}
/** Change the width of every keys so that the row is 's' units wide. */
public Row updateWidth(float newWidth)
{
float s = newWidth / keysWidth;
ArrayList<Key> keys_ = new ArrayList<Key>();
for (Key k : keys)
keys_.add(k.scaleWidth(s));
return new Row(keys_, height, shift);
}
}
public static class Key
@@ -131,8 +152,10 @@ class KeyboardData
public final float width;
/* Extra empty space on the left of the key. */
public final float shift;
/* Put keys 1 to 4 on the edges instead of the corners. */
public final boolean edgekeys;
public Key(KeyValue k0, KeyValue k1, KeyValue k2, KeyValue k3, KeyValue k4, float w, float s)
protected Key(KeyValue k0, KeyValue k1, KeyValue k2, KeyValue k3, KeyValue k4, float w, float s, boolean e)
{
key0 = k0;
key1 = k1;
@@ -141,6 +164,7 @@ class KeyboardData
key4 = k4;
width = w;
shift = s;
edgekeys = e;
}
public static Key parse(XmlResourceParser parser) throws Exception
@@ -152,14 +176,21 @@ class KeyboardData
KeyValue k4 = KeyValue.getKeyByName(parser.getAttributeValue(null, "key4"));
float width = parser.getAttributeFloatValue(null, "width", 1f);
float shift = parser.getAttributeFloatValue(null, "shift", 0.f);
boolean edgekeys = parser.getAttributeBooleanValue(null, "edgekeys", false);
while (parser.next() != XmlResourceParser.END_TAG)
continue ;
return new Key(k0, k1, k2, k3, k4, width, shift);
return new Key(k0, k1, k2, k3, k4, width, shift, edgekeys);
}
public Key replaceKeys(MapKeys f)
{
return new Key(f.map(key0), f.map(key1), f.map(key2), f.map(key3), f.map(key4), width, shift);
return new Key(f.map(key0), f.map(key1), f.map(key2), f.map(key3), f.map(key4), width, shift, edgekeys);
}
/** New key with the width multiplied by 's'. */
public Key scaleWidth(float s)
{
return new Key(key0, key1, key2, key3, key4, width * s, shift, edgekeys);
}
}
@@ -227,4 +258,22 @@ class KeyboardData
return k;
}
}
/** Parsing utils */
/** Returns [false] on [END_DOCUMENT] or [END_TAG], [true] otherwise. */
private static boolean expect_tag(XmlResourceParser parser, String name) throws Exception
{
int status;
do
{
status = parser.next();
if (status == XmlResourceParser.END_DOCUMENT || status == XmlResourceParser.END_TAG)
return false;
}
while (status != XmlResourceParser.START_TAG);
if (!parser.getName().equals(name))
throw new Exception("Unknow tag: " + parser.getName());
return true;
}
}

View File

@@ -0,0 +1,290 @@
package juloo.keyboard2;
import android.os.Handler;
import android.os.Message;
import java.util.ArrayList;
/**
* Manage pointers (fingers) on the screen and long presses.
* Call back to IPointerEventHandler.
*/
public final class Pointers implements Handler.Callback
{
private Handler _keyrepeat_handler;
private ArrayList<Pointer> _ptrs = new ArrayList<Pointer>();
private IPointerEventHandler _handler;
private Config _config;
public Pointers(IPointerEventHandler h, Config c)
{
_keyrepeat_handler = new Handler(this);
_handler = h;
_config = c;
}
public int getFlags()
{
int flags = 0;
for (Pointer p : _ptrs)
flags |= p.flags;
return flags;
}
public void clear()
{
_ptrs.clear();
}
public boolean isKeyDown(KeyboardData.Key k)
{
for (Pointer p : _ptrs)
if (p.key == k)
return true;
return false;
}
/**
* These flags can be different:
* FLAG_LOCK Removed when the key is locked
* FLAG_LOCKED Added when the key is locked
* FLAG_LATCH Removed when the key is latched (released but not consumed yet)
* Returns [-1] if not found.
*/
public int getKeyFlags(KeyValue kv)
{
for (Pointer p : _ptrs)
if (p.value == kv)
return p.flags;
return -1;
}
// Receiving events
public void onTouchUp(int pointerId)
{
Pointer ptr = getPtr(pointerId);
if (ptr == null)
return;
stopKeyRepeat(ptr);
Pointer latched = getLatched(ptr.value);
if (latched != null) // Already latched
{
removePtr(ptr); // Remove dupplicate
if ((latched.flags & KeyValue.FLAG_LOCK) != 0) // Locking key, toggle lock
{
latched.flags = (latched.flags & ~KeyValue.FLAG_LOCK) | KeyValue.FLAG_LOCKED;
_handler.onPointerFlagsChanged();
}
else // Otherwise, unlatch
{
removePtr(latched);
_handler.onPointerUp(ptr.value);
}
}
else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
{
ptr.flags &= ~KeyValue.FLAG_LATCH;
ptr.pointerId = -1; // Latch
_handler.onPointerFlagsChanged();
}
else
{
clearLatched();
removePtr(ptr);
_handler.onPointerUp(ptr.value);
}
}
public void onTouchDown(float x, float y, int pointerId, KeyboardData.Key key)
{
KeyValue value = key.key0;
Pointer ptr = new Pointer(pointerId, key, value, x, y);
_ptrs.add(ptr);
if (value != null && (value.flags & KeyValue.FLAG_NOREPEAT) == 0)
startKeyRepeat(ptr);
_handler.onPointerDown(value);
}
public void onTouchMove(float x, float y, int pointerId)
{
Pointer ptr = getPtr(pointerId);
if (ptr == null)
return;
float dx = x - ptr.downX;
float dy = y - ptr.downY;
float dist = Math.abs(dx) + Math.abs(dy);
ptr.ptrDist = dist;
KeyValue newValue;
if (dist < _config.swipe_dist_px)
{
newValue = ptr.key.key0;
}
else if (ptr.key.edgekeys)
{
if (Math.abs(dy) > Math.abs(dx)) // vertical swipe
newValue = (dy < 0) ? ptr.key.key1 : ptr.key.key4;
else // horizontal swipe
newValue = (dx < 0) ? ptr.key.key3 : ptr.key.key2;
}
else
{
if (dx < 0) // left side
newValue = (dy < 0) ? ptr.key.key1 : ptr.key.key3;
else // right side
newValue = (dy < 0) ? ptr.key.key2 : ptr.key.key4;
}
if (newValue != null && newValue != ptr.value)
{
int old_flags = (ptr.value != null) ? ptr.value.flags : 0;
ptr.value = newValue;
ptr.flags = newValue.flags;
if ((old_flags & newValue.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
{
// Keep the keyrepeat going between modulated keys.
}
else
{
stopKeyRepeat(ptr);
if ((newValue.flags & KeyValue.FLAG_NOREPEAT) == 0)
startKeyRepeat(ptr);
_handler.onPointerSwipe(newValue);
}
}
}
// Pointers management
private Pointer getPtr(int pointerId)
{
for (Pointer p : _ptrs)
if (p.pointerId == pointerId)
return p;
return null;
}
private void removePtr(Pointer ptr)
{
_ptrs.remove(ptr);
}
private Pointer getLatched(KeyValue kv)
{
for (Pointer p : _ptrs)
if (p.value == kv && p.pointerId == -1)
return p;
return null;
}
private void clearLatched()
{
for (int i = _ptrs.size() - 1; i >= 0; i--)
{
Pointer ptr = _ptrs.get(i);
// Latched and not locked, remove
if (ptr.pointerId == -1 && (ptr.flags & KeyValue.FLAG_LOCKED) == 0)
_ptrs.remove(i);
// Not latched but pressed, don't latch once released
else if ((ptr.flags & KeyValue.FLAG_LATCH) != 0)
ptr.flags &= ~KeyValue.FLAG_LATCH;
}
}
// Key repeat
/** Message from [_keyrepeat_handler]. */
@Override
public boolean handleMessage(Message msg)
{
for (Pointer ptr : _ptrs)
{
if (ptr.timeoutWhat == msg.what)
{
long nextInterval = _config.longPressInterval;
if (_config.preciseRepeat && (ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
{
// Slower repeat for modulated keys
nextInterval *= 2;
// Modulate repeat interval depending on the distance of the pointer
nextInterval = (long)((float)nextInterval / modulatePreciseRepeat(ptr));
}
_keyrepeat_handler.sendEmptyMessageDelayed(msg.what, nextInterval);
_handler.onPointerHold(ptr.value);
return (true);
}
}
return (false);
}
private static int uniqueTimeoutWhat = 0;
private void startKeyRepeat(Pointer ptr)
{
int what = (uniqueTimeoutWhat++);
ptr.timeoutWhat = what;
long timeout = _config.longPressTimeout;
// Faster repeat timeout for modulated keys
if ((ptr.flags & KeyValue.FLAG_PRECISE_REPEAT) != 0)
timeout /= 2;
_keyrepeat_handler.sendEmptyMessageDelayed(what, timeout);
}
private void stopKeyRepeat(Pointer ptr)
{
if (ptr.timeoutWhat != -1)
{
_keyrepeat_handler.removeMessages(ptr.timeoutWhat);
ptr.timeoutWhat = -1;
ptr.repeatingPtrDist = -1.f;
}
}
private float modulatePreciseRepeat(Pointer ptr)
{
if (ptr.repeatingPtrDist < 0.f)
ptr.repeatingPtrDist = ptr.ptrDist; // First repeat
if (ptr.ptrDist > ptr.repeatingPtrDist * 2.f)
ptr.repeatingPtrDist = ptr.ptrDist / 2.f; // Large swipe, move the middle point
float left = ptr.repeatingPtrDist / 2.f;
float accel = (ptr.ptrDist - left) / (ptr.repeatingPtrDist - left);
return Math.min(8.f, Math.max(0.1f, accel));
}
private final class Pointer
{
/** -1 when latched. */
public int pointerId;
public KeyboardData.Key key;
public KeyValue value;
public float downX;
public float downY;
/** Distance of the pointer to the initial press. */
public float ptrDist;
public int flags;
/** Identify timeout messages. */
public int timeoutWhat;
/** ptrDist at the first repeat, -1 otherwise. */
public float repeatingPtrDist;
public Pointer(int p, KeyboardData.Key k, KeyValue v, float x, float y)
{
pointerId = p;
key = k;
value = v;
downX = x;
downY = y;
ptrDist = 0.f;
flags = (v == null) ? 0 : v.flags;
timeoutWhat = -1;
repeatingPtrDist = -1.f;
}
}
public interface IPointerEventHandler
{
public void onPointerDown(KeyValue k);
public void onPointerSwipe(KeyValue k);
public void onPointerUp(KeyValue k);
public void onPointerFlagsChanged();
public void onPointerHold(KeyValue k);
}
}

View File

@@ -54,10 +54,10 @@ public class Theme
return p;
}
public Paint subLabelPaint(boolean special_font, boolean align_right)
public Paint subLabelPaint(boolean special_font, Paint.Align align)
{
Paint p = special_font ? _specialKeySubLabelPaint : _keySubLabelPaint;
p.setTextAlign(align_right ? Paint.Align.RIGHT : Paint.Align.LEFT);
p.setTextAlign(align);
return p;
}