Compare commits

..

1 Commits

Author SHA1 Message Date
Jules Aguillon
3952f48b13 Add icons to some options
Co-authored-by: RetrogisusDEV
2023-08-15 20:37:00 +02:00
405 changed files with 5959 additions and 23189 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
srcs/juloo.keyboard2/ComposeKeyData.java -diff

View File

@ -1,26 +1,19 @@
name: Check layouts name: Check layouts
# Runs 'gen_layouts.py' and checks that the generated file were uptodate.
# This doesn't run 'check_layout.py'.
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
pull_request: pull_request:
jobs: jobs:
gen-layouts:
name: Generated files
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- run: python3 gen_layouts.py
- name: "Check that the generated 'layouts.xml' is uptodate, otherwise run 'python3 gen_layouts.py'"
run: git diff --exit-code
check-layouts: check-layouts:
name: check_layout.output
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- run: python3 check_layout.py - run: python3 gen_layouts.py
- name: "Check that the generated 'check_layout.output' is uptodate, otherwise run 'python3 check_layout.py'" - name: Check that the generated layouts.xml is uptodate, run python3 gen_layouts.py otherwise
run: git diff --exit-code run: git diff --exit-code

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- run: python3 sync_translations.py - run: python3 sync_translations.py
- name: "Check that strings files are uptodate, otherwise run 'python3 sync_translations.py'" - name: Check that strings files are uptodate, run python3 sync_translations.py otherwise
run: git add -N . && git diff --exit-code run: git diff --exit-code

View File

@ -9,41 +9,39 @@ jobs:
Build-Apk: Build-Apk:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/setup-java@v4 - uses: actions/setup-java@v3
with: with:
distribution: 'zulu' # See 'Supported distributions' for available options distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17' java-version: '11'
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Restore debug keystore from GitHub Secrets - name: Cache debug certificate
uses: actions/cache@v3
with:
path: _build/debug.keystore
key: debug-keystore
- name: Restore debug keystore from github Secrets
run: | run: |
mkdir -p _build
cd "_build"
# Check if exist and use the secret named DEBUG_KEYSTORE # Check if exist and use the secret named DEBUG_KEYSTORE
# The contents of the secret can be obtained - # The contents of the secret can be obtained -
# from the debug.keystore.asc from you local folder # from the debug.keystore.asc from you local _build folder
# (refer to CONTRIBUTING.md Using the local debug.keystore on the Github CI actions)
if [[ ! "${{ secrets.DEBUG_KEYSTORE }}" = "" ]]; then if [[ ! "${{ secrets.DEBUG_KEYSTORE }}" = "" ]]; then
echo "${{ secrets.DEBUG_KEYSTORE }}" > "debug.keystore.asc" echo "${{ secrets.DEBUG_KEYSTORE }}" > "debug.keystore.asc"
if [[ -s "debug.keystore.asc" ]]; then if [[ -s "debug.keystore.asc" ]]; then
echo "Restoring debug keystore from GitHub secrets"
gpg -d --passphrase "debug0" --batch "debug.keystore.asc" > "debug.keystore" gpg -d --passphrase "debug0" --batch "debug.keystore.asc" > "debug.keystore"
fi fi
fi fi
- name: Build debug APK - name: Build
uses: gradle/gradle-build-action@v3 run: make
env:
DEBUG_KEYSTORE: "debug.keystore"
DEBUG_KEYSTORE_PASSWORD: debug0
DEBUG_KEY_ALIAS: debug
DEBUG_KEY_PASSWORD: debug0
with:
arguments: assembleDebug
- name: Artifact naming - name: Artifact naming
run: | run: |
artifact="${{github.repository_owner}} ${{github.ref_name}}" artifact="${{github.repository_owner}} ${{github.ref_name}}"
artifact="${artifact//\//-}" # replace slashes artifact="${artifact//\//-}" # replace slashes
echo "artifact=${artifact}" >> $GITHUB_ENV echo "artifact=${artifact}" >> $GITHUB_ENV
- name: Upload debug APK - name: Save debug apk
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: "${{env.artifact}} debug_apk" name: "${{env.artifact}} debug_apk"
path: build/outputs/apk/debug/*.apk path: _build/*.apk

10
.gitignore vendored
View File

@ -1,12 +1,4 @@
*.keystore *.keystore
*.keystore.asc *.keystore.asc
_build
/*-keystore.conf /*-keystore.conf
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/captures
/build
# Directory _build is not used anymore
/_build

View File

@ -1,32 +1,25 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:hardwareAccelerated="false"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="juloo.keyboard2" android:versionCode="35" android:versionName="1.23.0" android:hardwareAccelerated="false">
<uses-permission android:name="android.permission.VIBRATE"/> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="33"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <application android:label="@string/app_name" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:hardwareAccelerated="false">
<application android:label="@string/app_name" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:hardwareAccelerated="false">
<service android:name="juloo.keyboard2.Keyboard2" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD" android:exported="true" android:directBootAware="true"> <service android:name="juloo.keyboard2.Keyboard2" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD" android:exported="true" android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.view.InputMethod"/> <action android:name="android.view.InputMethod"/>
</intent-filter> </intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method"/> <meta-data android:name="android.view.im" android:resource="@xml/method"/>
</service> </service>
<activity android:name="juloo.keyboard2.SettingsActivity" android:icon="@mipmap/ic_launcher" android:label="@string/settings_activity_label" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true"> <activity android:name="juloo.keyboard2.SettingsActivity" android:icon="@drawable/ic_launcher" android:label="@string/settings_activity_label" android:theme="@style/android:Theme.DeviceDefault" android:exported="true" android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="juloo.keyboard2.LauncherActivity" android:icon="@mipmap/ic_launcher" android:theme="@style/appTheme" android:exported="true" android:directBootAware="true"> <activity android:name="juloo.keyboard2.LauncherActivity" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/android:Theme.DeviceDefault" android:exported="true" android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- To query enabled input methods for voice IME detection --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<queries>
<intent>
<action android:name="android.view.InputMethod" />
</intent>
</queries>
</manifest> </manifest>

View File

@ -4,46 +4,52 @@ Thanks for contributing :)
## Building the app ## Building the app
The application uses Gradle and can be used with Android Studio, but using The application doesn't use Gradle and it might be hard to use some features of
Android Studio is not required. The build dependencies are: Android Studio.
- OpenJDK 17
Fortunately, there's not many dependencies:
- OpenJDK 8
- Android SDK: build tools (minimum `28.0.1`), platform `30` - Android SDK: build tools (minimum `28.0.1`), platform `30`
- Make sure to have the `$ANDROID_HOME` environment variable set.
Python 3 is required to update generated files but not to build the app.
For Android Studio users, no more setup is needed.
For Nix users, the right environment can be obtained with `nix-shell ./shell.nix`. For Nix users, the right environment can be obtained with `nix-shell ./shell.nix`.
Instructions to install Nix are [here](https://wiki.nixos.org/wiki/Nix_Installation_Guide). Instructions to install Nix are [here](https://nixos.wiki/wiki/Nix_Installation_Guide).
If you don't use Android Studio or Nix, you have to inform Gradle about the
location of your Android SDK by either:
- Setting the `ANDROID_HOME` environment variable to point to the android sdk or
- Creating the file `local.properties` and writing
`sdk.dir=<location_of_android_home>` into it.
Building the debug apk: Building the debug apk:
```sh ```sh
./gradlew assembleDebug make
``` ```
Nix users can call gradle directly: `gradle assembleDebug`. If the build succeed, the debug apk is located in `_build/juloo.keyboard2.debug.apk`.
If the build succeeds, the debug apk is located in `build/outputs/apk/debug/app-debug.apk`. ## Using the local debug.keystore on the Github CI actions
It's possible to save the local debug.keystore into a github secret, so the same keystore is utilized to build the debug apk in the CI github actions.
Doing this, they wil have the same signature, thus the debug apk can be updated without having to uninstall it first.
After you sucessfully run `make`, (thus a debug.keystore exists) you can use this second command to generate a base64 stringified version of it
```sh
cd _build
gpg -c --armor --pinentry-mode loopback --passphrase debug0 --yes "debug.keystore"
```
A file will be generated inside the local `_build/` folder, called `debug.keystore.asc`
You can copy the content of this file, and with that, paste it into a new github secret in your repo settings.
The secret must be named `DEBUG_KEYSTORE`
## Debugging on your phone ## Debugging on your phone
First [Enable adb debugging on your device](https://developer.android.com/studio/command-line/adb#Enabling). First [Enable adb debugging on your device](https://developer.android.com/studio/command-line/adb#Enabling).
Then connect your phone to your computer using an USB cable or via wireless Then connect your phone to your computer using an USB cable or wireless
debugging. debugging.
If you use Android Studio, this process will be automatic and you don't have to
follow this guide anymore.
And finally, install the application with: And finally, install the application with:
```sh ```sh
./gradlew installDebug make installd
``` ```
The released version of the application won't be removed, both versions will The released version of the application won't be removed, both versions will
@ -51,13 +57,11 @@ be installed at the same time.
## Debugging the application: INSTALL_FAILED_UPDATE_INCOMPATIBLE ## Debugging the application: INSTALL_FAILED_UPDATE_INCOMPATIBLE
`./gradlew installDebug` can fail with the following error message: `make installd` can fail with the following error message:
``` ```
FAILURE: Build failed with an exception. adb: failed to install _build/juloo.keyboard2.debug.apk: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package juloo.keyboard2.debug signatures do not match previously installed version; ignoring!]
* What went wrong: make: *** [Makefile:20: installd] Error 1
Execution failed for task ':installDebug'.
> java.util.concurrent.ExecutionException: com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package juloo.keyboard2.debug signatures do not match newer version; ignoring!
``` ```
The application can't be "updated" because the temporary certificate has been The application can't be "updated" because the temporary certificate has been
@ -66,57 +70,36 @@ The application must be enabled again in the settings.
```sh ```sh
adb uninstall juloo.keyboard2.debug adb uninstall juloo.keyboard2.debug
./gradlew installDebug make installd
``` ```
## Specifying a debug signing certificate on Github Actions
It's possible to specify the signing certificate that the automated build
should use.
After you successfully run `./gradlew asssembleDebug`, (thus a debug.keystore
exists) you can use this second command to generate a base64 stringified
version of it:
```sh
gpg -c --armor --pinentry-mode loopback --passphrase debug0 --yes "debug.keystore"
```
This will create the file `debug.keystore.asc`, paste its content into a new
Github secret named `DEBUG_KEYSTORE`.
## Guidelines ## Guidelines
### Adding a layout ### Adding a layout
Layouts are defined in XML, see `srcs/layouts/latn_qwerty_us.xml`. Layouts are defined in XML, see `res/xml/latn_qwerty_us.xml`.
An online tool for editing layout files written by @Lixquid is available An online tool for editing layout files written by @Lixquid is available
[here](https://unexpected-keyboard-layout-editor.lixquid.com/). [here](https://unexpected-keyboard-layout-editor.lixquid.com/).
Makes sure to specify the `name` attribute like in `latn_qwerty_us.xml`, Makes sure to specify the `name` attribute like in `latn_qwerty_us.xml`,
otherwise the layout won't be added to the app. otherwise the layout won't be added to the app.
The layout file must be placed in the `srcs/layouts` directory and named The layout file must be placed in the `res/xml/` directory and named according to:
according to:
- script (`latn` for latin, etc..) - script (`latn` for latin, etc..)
- layout name (eg. the name of a standard) - layout name (eg. the name of a standard)
- country code (or language code if more adequate) - country code (or language code if more adequate)
Then, run `./gradlew genLayoutsList` to add the layout to the app. Then, run `make gen_layouts` to add the layout to the app.
The last step will update the file `res/values/layouts.xml`, that you should The last step will update the file `res/values/layouts.xml`, that you should
not edit directly. not edit directly.
Run `./gradlew checkKeyboardLayouts` to check some properties about your Run `make check_layouts` to check some properties about your layout. This will
layout. This will change the file `check_layout.output`, which you should change the file `check_layout.output`, which you should commit.
commit.
Layouts are CC0 licensed by default. If you do not want your layout to be
released into the public domain, add a copyright notice at the top of the file
and a mention in `srcs/layouts/LICENSE`.
#### Adding a programming layout #### Adding a programming layout
A programming layout must contain all ASCII characters. A programming layout must contains every ASCII characters.
The current programming layouts are: QWERTY, Dvorak and Colemak. The current programming layouts are: QWERTY, Dvorak and Colemak.
See for example, Dvorak, added in https://github.com/Julow/Unexpected-Keyboard/pull/16 See for example, Dvorak, added in https://github.com/Julow/Unexpected-Keyboard/pull/16
@ -150,52 +133,32 @@ and other extra keys to show.
The list of language tags (generally two letters) The list of language tags (generally two letters)
and locales (generally of the form `xx_XX`) and locales (generally of the form `xx_XX`)
can be found in this [stackoverflow answer](https://stackoverflow.com/a/7989085) can be found in this stackoverflow answer: https://stackoverflow.com/a/7989085
### Updating translations ### Translations
The text used in the app is written in `res/values-<language_tag>/strings.xml`. Translations are always welcome !
The list of language tags can be found in this See for example: 1723288 (Latvian), baf867a (French).
[stackoverflow answer](https://stackoverflow.com/a/7989085) The app can be translated by writing `res/values-<language code>/strings.xml`
(for example `values-fr`, `values-lv`), based on the default:
The first part before the `_` is used, for example, `res/values/strings.xml` (English).
`res/values-fr/strings.xml` for French,
`res/values-lv/strings.xml` for Latvian.
Commented-out lines indicate missing translations:
```xml
<!-- <string name="pref_layouts_add">Add an alternate layout</string> -->
```
Remove the `<!--` and `-->` parts and change the text.
### Adding a translation
The `res/values-<language_tag>/strings.xml` file must be created by copying the
default translation in `res/values/strings.xml`, which contain the structure of
the file and the English strings.
To check that `strings.xml` is formatted correctly, run To check that `strings.xml` is formatted correctly, run
`python sync_translations.py`. This will modify your files. `python sync_translations.py`. This will modify your files.
Store descriptions in `fastlane/metadata/android/` are updated automatically. The store description is found in `metadata/android/<locale>/`,
`short_description.txt` and `full_description.txt`.
The short description must not exceed 80 characters.
Translating changelogs is not useful. Translating changelogs is not useful.
The app name might be partially translated, the "Unexpected" word should remain The app name might be partially translated, the "unexpected" word should remain
untranslated if possible. untranslated.
As translations need to be updated regularly, you can subscribe to this issue As translations need to be updated regularly, you can subscribe to this issue
to receive a notification when an update is needed: to receive a notification when an update is needed:
https://github.com/Julow/Unexpected-Keyboard/issues/373 https://github.com/Julow/Unexpected-Keyboard/issues/373
### Adding Compose key sequences
New Compose sequences can be added into `srcs/compose/compose/extra.json`.
If a entirely new family of sequences were to be added, a new `.json` file can
be created in the same directory to host them.
### Adding key combinations ### Adding key combinations
Key combinations are defined in `srcs/juloo.keyboard2/KeyModifier.java`. Key combinations are defined in `srcs/juloo.keyboard2/KeyModifier.java`.

View File

@ -1,3 +0,0 @@
github: [ Julow ]
liberapay: Julow
custom: [ "https://paypal.me/JulesAguillon" ]

139
Makefile Normal file
View File

@ -0,0 +1,139 @@
# Configuration
PACKAGE_NAME = juloo.keyboard2
ANDROID_PLATFORM_VERSION = android-30
JAVA_VERSION = 1.7
SRC_DIR = srcs
RES_DIR = res
EXTRA_JARS =
# /
debug: _build/$(PACKAGE_NAME).debug.apk
release: _build/$(PACKAGE_NAME).apk
installd: _build/$(PACKAGE_NAME).debug.apk
adb install -r "$<"
clean:
rm -rf _build/*.dex _build/class _build/gen _build/*.apk _build/*.unsigned-apk \
_build/*.idsig _build/assets
rebuild_special_font: _build/special_font.ttf
cp "$<" srcs/special_font/result.ttf
sync_translations:
python sync_translations.py
check_layouts:
python check_layout.py $(wildcard res/xml/*.xml) > check_layout.output
gen_layouts:
python gen_layouts.py
# Will modify the source tree.
runtest: rebuild_special_font sync_translations check_layouts gen_layouts
.PHONY: release debug installd clean rebuild_special_font check_layouts \
sync_translations runtest gen_layouts
$(shell mkdir -p _build)
ifndef ANDROID_HOME
$(error ANDROID_HOME not set)
endif
ANDROID_BUILD_TOOLS = $(lastword $(sort $(wildcard $(ANDROID_HOME)/build-tools/*)))
ANDROID_PLATFORM = $(ANDROID_HOME)/platforms/$(ANDROID_PLATFORM_VERSION)
ifeq ($(shell [ -d "$(ANDROID_PLATFORM)" ] && echo ok),)
$(error Android platform not found. Want $(ANDROID_PLATFORM_VERSION), \
found $(notdir $(wildcard $(ANDROID_HOME)/platforms/*)))
endif
JAVAC_FLAGS = -source $(JAVA_VERSION) -target $(JAVA_VERSION) -encoding utf8
# Source files
MANIFEST_FILE = AndroidManifest.xml
JAVA_FILES = $(shell find $(SRC_DIR) -name '*.java')
RES_FILES = $(shell find $(RES_DIR) -type f)
# Debug signing
DEBUG_KEYSTORE = _build/debug.keystore
DEBUG_PASSWD = debug0
$(DEBUG_KEYSTORE):
echo y | keytool -genkeypair -dname "cn=d, ou=e, o=b, c=ug" \
-alias debug -keypass $(DEBUG_PASSWD) -keystore "$@" \
-keyalg rsa -storepass $(DEBUG_PASSWD) -validity 10000
_build/%.debug.apk: _build/%.debug.unsigned-apk $(DEBUG_KEYSTORE)
$(ANDROID_BUILD_TOOLS)/apksigner sign --in "$<" --out "$@" \
--ks $(DEBUG_KEYSTORE) --ks-key-alias debug --ks-pass "pass:$(DEBUG_PASSWD)"
# Debug apk
_build/$(PACKAGE_NAME).debug.unsigned-apk: AAPT_PACKAGE_FLAGS+=--rename-manifest-package $(PACKAGE_NAME).debug --product debug
# Release signing
# %-keystore.conf should declare KEYSTORE, KEYNAME and KEYSTOREPASS
# it is interpreted as a shell script
_build/%.apk: _build/%.unsigned-apk %-keystore.conf
eval `cat $(word 2,$^)` && \
$(ANDROID_BUILD_TOOLS)/apksigner sign --in "$<" --out "$@" \
--ks "$$KEYSTORE" --ks-key-alias "$$KEYNAME" --ks-pass "pass:$$KEYSTOREPASS"
# Package
_build/%.unsigned-apk: _build/%.unaligned-apk
$(ANDROID_BUILD_TOOLS)/zipalign -fp 4 "$<" "$@"
APK_EXTRA_FILES = classes.dex assets/special_font.ttf
_build/%.unaligned-apk: $(addprefix _build/,$(APK_EXTRA_FILES)) $(MANIFEST_FILE)
$(ANDROID_BUILD_TOOLS)/aapt package -f -M $(MANIFEST_FILE) -S $(RES_DIR) \
-I $(ANDROID_PLATFORM)/android.jar -F "$@" $(AAPT_PACKAGE_FLAGS)
cd $(@D) && $(ANDROID_BUILD_TOOLS)/aapt add $(@F) $(APK_EXTRA_FILES)
# Copy the special font file into _build because aapt requires relative paths
_build/assets/special_font.ttf: srcs/special_font/result.ttf
mkdir -p $(@D)
cp "$<" "$@"
# R.java
GEN_DIR = _build/gen
R_FILE = $(GEN_DIR)/$(subst .,/,$(PACKAGE_NAME))/R.java
$(R_FILE): $(RES_FILES) $(MANIFEST_FILE)
mkdir -p "$(@D)"
$(ANDROID_BUILD_TOOLS)/aapt package -f -m -S $(RES_DIR) -J $(GEN_DIR) \
-M $(MANIFEST_FILE) -I $(ANDROID_PLATFORM)/android.jar
# Compile java classes and build classes.dex
OBJ_DIR = _build/class
# A$B.class files are ignored
# CLASS_FILES = $(JAVA_FILES:$(SRC_DIR)/%.java=$(OBJ_DIR)/%.class) \
# $(R_FILE:$(GEN_DIR)/%.java=$(OBJ_DIR)/%.class)
_build/classes.dex: $(JAVA_FILES) $(R_FILE)
mkdir -p $(OBJ_DIR)
javac -d $(OBJ_DIR) $(JAVAC_FLAGS) \
-classpath $(ANDROID_PLATFORM)/android.jar:$(EXTRA_JARS) \
-sourcepath $(SRC_DIR):$(GEN_DIR) \
$^
$(ANDROID_BUILD_TOOLS)/d8 --output $(@D) $(OBJ_DIR)/*/*/* $(subst :, ,$(EXTRA_JARS))
# Font file
FONT_GLYPHS = $(wildcard srcs/special_font/*.svg)
_build/special_font.ttf: srcs/special_font/build.pe $(FONT_GLYPHS)
fontforge -lang=ff -script $< "$@" $(FONT_GLYPHS)

View File

@ -9,7 +9,9 @@
Lightweight and privacy-conscious virtual keyboard for Android. Lightweight and privacy-conscious virtual keyboard for Android.
https://github.com/Julow/Unexpected-Keyboard/assets/2310568/28f8f6fe-ac13-46f3-8c5e-d62443e16d0d | <img src="/metadata/android/en-US/images/phoneScreenshots/1.png" alt="Screenshot-1" /> | <img src="/metadata/android/en-US/images/phoneScreenshots/2.png" alt="Screenshot-2"/> | <img src="/metadata/android/en-US/images/phoneScreenshots/3.png" alt="Screenshot-3"/> |
| --- | --- | --- |
| <img src="/metadata/android/en-US/images/phoneScreenshots/4.png" alt="Screenshot-4" /> | <img src="/metadata/android/en-US/images/phoneScreenshots/5.png" alt="Screenshot-5" /> | <img src="/metadata/android/en-US/images/phoneScreenshots/6.png" alt="Screenshot-6" /> |
The main feature is that you can type more characters by swiping the keys towards the corners. The main feature is that you can type more characters by swiping the keys towards the corners.
@ -18,14 +20,8 @@ Now perfect for everyday use.
This application contains no ads, doesn't make any network requests and is Open Source. This application contains no ads, doesn't make any network requests and is Open Source.
Usage: to apply the symbols located in the corners of each key, slide your finger in the direction of the symbols. For example, the Settings are opened by sliding in the left down corner.
| <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" alt="Screenshot-1" /> | <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" alt="Screenshot-2"/> | <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png" alt="Screenshot-3"/> |
| --- | --- | --- |
| <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png" alt="Screenshot-4" /> | <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png" alt="Screenshot-5" /> | <img src="/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png" alt="Screenshot-6" /> |
## Similar apps ## Similar apps
* [Calculator++](https://git.bubu1.eu/Bubu/android-calculatorpp) - Calculator with a similar UX, swipe to corners for advanced math symbols and operators. * [Calculator++](https://github.com/Bubu/android-calculatorpp) - Calculator with a similar UX, swipe to corners for advanced math symbols and operators. Works up to Android 13 but maybe unmaintained.
## Contributing ## Contributing

Binary file not shown.

View File

@ -1,199 +0,0 @@
plugins {
id 'com.android.application' version '8.1.1'
}
dependencies {
testImplementation "junit:junit:4.13.2"
}
android {
namespace 'juloo.keyboard2'
compileSdk 34
defaultConfig {
applicationId "juloo.keyboard2"
minSdk 11
targetSdkVersion 35
versionCode 42
versionName "1.29.1"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['srcs/juloo.keyboard2']
res.srcDirs = ['res', 'build/generated-resources']
assets.srcDirs = ['assets']
}
test {
java.srcDirs = ['test']
}
}
signingConfigs {
// Debug builds will always be signed. If no environment variables are set, a default
// keystore will be initialized by the task initDebugKeystore and used. This keystore
// can be uploaded to GitHub secrets by following instructions in CONTRIBUTING.md
// in order to always receive correctly signed debug APKs from the CI.
debug {
storeFile(System.env.DEBUG_KEYSTORE ? file(System.env.DEBUG_KEYSTORE) : file("debug.keystore"))
storePassword(System.env.DEBUG_KEYSTORE_PASSWORD ? "$System.env.DEBUG_KEYSTORE_PASSWORD" : "debug0")
keyAlias(System.env.DEBUG_KEY_ALIAS ? "$System.env.DEBUG_KEY_ALIAS" : "debug")
keyPassword(System.env.DEBUG_KEY_PASSWORD ? "$System.env.DEBUG_KEY_PASSWORD" : "debug0")
}
release {
if (System.env.RELEASE_KEYSTORE) {
storeFile file(System.env.RELEASE_KEYSTORE)
storePassword "$System.env.RELEASE_KEYSTORE_PASSWORD"
keyAlias "$System.env.RELEASE_KEY_ALIAS"
keyPassword "$System.env.RELEASE_KEY_PASSWORD"
}
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
resValue "string", "app_name", "@string/app_name_release"
signingConfig signingConfigs.release
}
debug {
minifyEnabled false
shrinkResources false
debuggable true
applicationIdSuffix ".debug"
resValue "string", "app_name", "@string/app_name_debug"
resValue "bool", "debug_logs", "true"
signingConfig signingConfigs.debug
}
}
// Name outputs after the application ID.
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${applicationId}.apk"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
// Translation are already checked by 'syncTranslations'
disable 'MissingTranslation'
}
}
tasks.register('buildKeyboardFont') {
println "\nBuilding assets/special_font.ttf"
mkdir "$buildDir"
exec {
workingDir "$projectDir/srcs/special_font"
def svgFiles = workingDir.listFiles().findAll {
it.isFile() && it.name.endsWith(".svg")
}
commandLine("fontforge", "-lang=ff", "-script", "build.pe", "$buildDir/special_font.ttf", *svgFiles)
}
copy {
from "$buildDir/special_font.ttf"
into "assets"
}
}
tasks.register('genEmojis') {
println "\nGenerating res/raw/emojis.txt"
exec {
workingDir = projectDir
commandLine "python", "gen_emoji.py"
}
}
tasks.withType(Test).configureEach {
dependsOn 'genLayoutsList'
dependsOn 'checkKeyboardLayouts'
dependsOn 'syncTranslations'
dependsOn 'compileComposeSequences'
}
tasks.register('genLayoutsList') {
println "\nGenerating res/values/layouts.xml"
exec {
workingDir = projectDir
commandLine "python", "gen_layouts.py"
}
}
tasks.register('checkKeyboardLayouts') {
println "\nChecking layouts"
exec {
def layouts = new File(projectDir, "srcs/layouts").listFiles().findAll {
it.name.endsWith(".xml")
}
workingDir = projectDir
commandLine("python", "check_layout.py", *layouts)
standardOutput = new FileOutputStream("${projectDir}/check_layout.output")
}
}
tasks.register('syncTranslations') {
println "\nUpdating translations"
exec {
workingDir = projectDir
commandLine "python", "sync_translations.py"
}
}
tasks.register('compileComposeSequences') {
def out = "srcs/juloo.keyboard2/ComposeKeyData.java"
println "\nGenerating ${out}"
exec {
def sequences = new File(projectDir, "srcs/compose").listFiles().findAll {
!it.name.endsWith(".py") && !it.name.endsWith(".md")
}
workingDir = projectDir
commandLine("python", "srcs/compose/compile.py", *sequences)
standardOutput = new FileOutputStream("${projectDir}/${out}")
}
}
tasks.named("preBuild") {
dependsOn += "initDebugKeystore"
dependsOn += "copyRawQwertyUS"
dependsOn += "copyLayoutDefinitions"
}
tasks.register('initDebugKeystore') {
if (!file("debug.keystore").exists()) {
println "Initializing default debug keystore"
exec {
// A shell script might be needed if this line requires input from the user
commandLine "keytool", "-genkeypair", "-dname", "cn=d, ou=e, o=b, c=ug", "-alias", "debug", "-keypass", "debug0", "-keystore", "debug.keystore", "-keyalg", "rsa", "-storepass", "debug0", "-validity", "10000"
}
}
}
// latn_qwerty_us is used as a raw resource by the custom layout option.
tasks.register('copyRawQwertyUS')
{
copy {
from "srcs/layouts/latn_qwerty_us.xml"
into "build/generated-resources/raw"
}
}
tasks.register('copyLayoutDefinitions')
{
copy {
from "srcs/layouts"
include "*.xml"
into "build/generated-resources/xml"
}
}

View File

@ -1,148 +1,107 @@
# arab_alt # res/xml/arab_alt.xml
Layout includes some ASCII punctuation but not all, missing: !, ", ', +, -, /, :, ;, <, =, >, ?, [, \, ], _, |, ~ Layout includes some ASCII punctuation but not all, missing: !, ", ', +, -, /, :, ;, <, =, >, ?, [, \, ], _, |, ~
1 warnings Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
# arab_hamvaj_tly
Layout includes some ASCII punctuation but not all, missing: ", %, ', ,, /, ;, <, =, >, ?, [, \, ], _, `, {, |, }
Layout doesn't define some important keys, missing: loc esc
2 warnings 2 warnings
# arab_pc # res/xml/arab_pc_ckb.xml
Layout includes some ASCII punctuation but not all, missing: !, ', +, ;, ?, \, |
1 warnings
# arab_pc_ckb
Layout includes some ASCII punctuation but not all, missing: ", %, ', +, ,, ., :, ;, <, =, >, ?, `, |, ~ Layout includes some ASCII punctuation but not all, missing: ", %, ', +, ,, ., :, ;, <, =, >, ?, `, |, ~
1 warnings 1 warnings
# arab_pc_hindu # res/xml/arab_pc_ir.xml
Duplicate keys: (, )
Layout includes some ASCII punctuation but not all, missing: ", %, ', ,, /, ;, <, =, >, ?, [, \, ], `, {, |, }
2 warnings
# res/xml/arab_pc.xml
Layout includes some ASCII punctuation but not all, missing: !, ', +, ;, ?, \, | Layout includes some ASCII punctuation but not all, missing: !, ', +, ;, ?, \, |
1 warnings 1 warnings
# arab_pc_ir # res/xml/beng_national.xml
Layout includes some ASCII punctuation but not all, missing: ", %, ', ,, /, ;, <, =, >, ?, [, \, ], `, {, |, }
1 warnings
# armenian_ph_am
0 warnings
# beng_national
Layout includes some ASCII punctuation but not all, missing: $ Layout includes some ASCII punctuation but not all, missing: $
1 warnings Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
# beng_provat Layout doesn't specify a script.
3 warnings
# res/xml/beng_provat.xml
Layout includes some ASCII punctuation but not all, missing: $, &, *, ., /, <, >, [, \, ], `, {, |, } Layout includes some ASCII punctuation but not all, missing: $, &, *, ., /, <, >, [, \, ], `, {, |, }
1 warnings Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
# cyrl_jcuken_ru Layout doesn't specify a script.
3 warnings
# res/xml/cyrl_jcuken_ru.xml
0 warnings 0 warnings
# cyrl_jcuken_uk # res/xml/cyrl_jcuken_uk.xml
0 warnings 0 warnings
# cyrl_lynyertz_sr # res/xml/cyrl_yaverti.xml
0 warnings
# cyrl_ueishsht
0 warnings
# cyrl_yaverti
Layout includes some ASCII punctuation but not all, missing: ~ Layout includes some ASCII punctuation but not all, missing: ~
1 warnings 1 warnings
# cyrl_yqukeng_tj # res/xml/deva_alt.xml
Layout doesn't define some important keys, missing: loc esc, loc tab Layout includes some ASCII punctuation but not all, missing: #, $, %, &, ', (, ), *, +, ., /, :, <, =, >, @, [, \, ], ^, _, `, {, |, }, ~
These keys are now added automatically, unexpected: f11_placeholder, f12_placeholder Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
2 warnings 2 warnings
# cyrl_yxukeng_os # res/xml/deva_inscript.xml
Layout includes some ASCII punctuation but not all, missing: ", #, $, &, ', @, [, ], ~ Duplicate keys: , ।
Layout doesn't define some important keys, missing: loc esc, loc tab
These keys are now added automatically, unexpected: f11_placeholder, f12_placeholder
3 warnings
# deva_alt
Layout includes some ASCII punctuation but not all, missing: #, $, %, &, ', (, ), +, ., /, :, <, =, >, [, \, ], ^, _, `, {, |, }, ~
Layout doesn't define some important keys, missing: loc esc, loc tab
2 warnings
# deva_inscript
Duplicate keys: ।
Layout includes some ASCII punctuation but not all, missing: ", $, ', ^, _, `, | Layout includes some ASCII punctuation but not all, missing: ", $, ', ^, _, `, |
2 warnings Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
# grek_qwerty 3 warnings
# res/xml/grek_qwerty.xml
Duplicate keys: ; Duplicate keys: ;
1 warnings 1 warnings
# guj_phonetic_in # res/xml/hang_dubeolsik_kr.xml
Duplicate keys: ટ, ડ 0 warnings
1 warnings # res/xml/hebr_1_il.xml
# hang_dubeolsik_kr
Layout doesn't define some important keys, missing: loc esc, loc tab
1 warnings
# hebr_1_il
Layout includes some ASCII punctuation but not all, missing: (, ), <, >, [, ], {, } Layout includes some ASCII punctuation but not all, missing: (, ), <, >, [, ], {, }
1 warnings 1 warnings
# hebr_2_il # res/xml/hebr_2_il.xml
Layout includes some ASCII punctuation but not all, missing: (, ), <, >, [, ], {, } Layout includes some ASCII punctuation but not all, missing: (, ), <, >, [, ], {, }
1 warnings 1 warnings
# latn_azerty_fr # res/xml/latn_azerty_fr.xml
0 warnings 0 warnings
# latn_bepo_fr # res/xml/latn_bone.xml
0 warnings Layout includes some ASCII punctuation but not all, missing: $
# latn_bone Layout redefines the bottom row but some important keys are missing, missing: switch_backward
Layout doesn't define some important keys, missing: loc esc, loc tab
Layout redefines the bottom row but some important keys are missing, missing: cursor_left, cursor_right, loc compose, loc end, loc home, loc page_down, loc page_up, loc switch_clipboard, loc switch_greekmath, loc voice_typing, switch_backward
2 warnings 2 warnings
# latn_colemak # res/xml/latn_colemak.xml
Some keys contain whitespaces, unexpected: ́
1 warnings
# latn_dvorak
0 warnings 0 warnings
# latn_neo2 # res/xml/latn_dvorak.xml
Layout redefines the bottom row but some important keys are missing, missing: loc switch_clipboard
1 warnings
# latn_qwerty_br
0 warnings 0 warnings
# latn_qwerty_cz # res/xml/latn_neo2.xml
Duplicate keys: -
Layout redefines the bottom row but some important keys are missing, missing: switch_forward
2 warnings
# res/xml/latn_qwerty_br.xml
0 warnings 0 warnings
# latn_qwerty_da # res/xml/latn_qwerty_es.xml
0 warnings 0 warnings
# latn_qwerty_es # res/xml/latn_qwerty_hu.xml
0 warnings 0 warnings
# latn_qwerty_gb # res/xml/latn_qwerty_lv.xml
0 warnings 0 warnings
# latn_qwerty_hu # res/xml/latn_qwerty_no.xml
0 warnings 0 warnings
# latn_qwerty_jp # res/xml/latn_qwerty_pl.xml
0 warnings 0 warnings
# latn_qwerty_lv # res/xml/latn_qwerty_ro.xml
0 warnings 0 warnings
# latn_qwerty_no # res/xml/latn_qwerty_se.xml
0 warnings
# latn_qwerty_pl
0 warnings
# latn_qwerty_ro
0 warnings
# latn_qwerty_se
Duplicate keys: !, ', ,, -, ., ? Duplicate keys: !, ', ,, -, ., ?
1 warnings 1 warnings
# latn_qwerty_sk # res/xml/latn_qwerty_tr.xml
Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
1 warnings
# res/xml/latn_qwerty_us.xml
0 warnings 0 warnings
# latn_qwerty_sr # res/xml/latn_qwerty_vi.xml
0 warnings Layout includes some ASCII punctuation but not all, missing: \
# latn_qwerty_tly 1 warnings
Duplicate keys: a, c, j, q # res/xml/latn_qwertz_cz_multifunctional.xml
Layout doesn't define some important keys, missing: loc esc, loc tab Layout includes some ASCII punctuation but not all, missing: `
Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
2 warnings 2 warnings
# latn_qwerty_tr # res/xml/latn_qwertz_cz.xml
0 warnings Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
# latn_qwerty_us
0 warnings
# latn_qwerty_vi
0 warnings
# latn_qwertz
0 warnings
# latn_qwertz_cz
0 warnings
# latn_qwertz_cz_multifunctional
Layout includes some ASCII punctuation but not all, missing: `
1 warnings 1 warnings
# latn_qwertz_de # res/xml/latn_qwertz_de.xml
0 warnings 0 warnings
# latn_qwertz_fr_ch # res/xml/latn_qwertz_hu.xml
0 warnings 0 warnings
# latn_qwertz_hu # res/xml/latn_qwertz_sk.xml
Layout includes some ASCII punctuation but not all, missing: \, `
Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
2 warnings
# res/xml/latn_qwertz.xml
0 warnings 0 warnings
# latn_qwertz_sk
Layout includes some ASCII punctuation but not all, missing: `
1 warnings
# shaw_imperial_en
0 warnings
# urdu_phonetic_ur
Duplicate keys:
Layout includes some ASCII punctuation but not all, missing: <, >, ?, `, |, ~
Some keys contain whitespaces, unexpected:
3 warnings

View File

@ -1,19 +1,12 @@
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import sys, os import sys
warning_count = 0 warning_count = 0
KNOWN_NOT_LAYOUT = set([ KNOWN_NOT_LAYOUT = set([
"number_row", "numpad", "pin", "res/xml/number_row.xml", "res/xml/numpad.xml", "res/xml/pin.xml",
"bottom_row", "settings", "method", "res/xml/bottom_row.xml", "res/xml/settings.xml", "res/xml/method.xml",
"greekmath", "numeric", "emoji_bottom_row", "res/xml/greekmath.xml", "res/xml/numeric.xml" ])
"clipboard_bottom_row" ])
KEY_ATTRIBUTES = set([
"key0",
"key1", "key2", "key3", "key4", "key5", "key6", "key7", "key8",
"nw", "ne", "sw", "se", "w", "e", "n", "s"
])
def warn(msg): def warn(msg):
global warning_count global warning_count
@ -41,15 +34,6 @@ def unexpected_keys(keys, symbols, msg):
if len(unexpected) > 0: if len(unexpected) > 0:
warn("%s, unexpected: %s" % (msg, key_list_str(unexpected))) warn("%s, unexpected: %s" % (msg, key_list_str(unexpected)))
# Write to [keys] and [dup].
def parse_row_from_et(row, keys, dup):
for key in row:
for attr in key.keys():
if attr in KEY_ATTRIBUTES:
k = key.get(attr).removeprefix("\\")
if k in keys: dup.add(k)
keys.add(k)
def parse_layout(fname): def parse_layout(fname):
keys = set() keys = set()
dup = set() dup = set()
@ -57,16 +41,12 @@ def parse_layout(fname):
if root.tag != "keyboard": if root.tag != "keyboard":
return None return None
for row in root: for row in root:
parse_row_from_et(row, keys, dup) for key in row:
return root, keys, dup for attr in key.keys():
if attr.startswith("key"):
def parse_row(fname): k = key.get(attr).removeprefix("\\")
keys = set() if k in keys: dup.add(k)
dup = set() keys.add(k)
root = ET.parse(fname).getroot()
if root.tag != "row":
return None
parse_row_from_et(root, keys, dup)
return root, keys, dup return root, keys, dup
def check_layout(layout): def check_layout(layout):
@ -75,22 +55,24 @@ def check_layout(layout):
missing_some_of(keys, "~!@#$%^&*(){}`[]=\\-_;:/.,?<>'\"+|", "ASCII punctuation") missing_some_of(keys, "~!@#$%^&*(){}`[]=\\-_;:/.,?<>'\"+|", "ASCII punctuation")
missing_some_of(keys, "0123456789", "digits") missing_some_of(keys, "0123456789", "digits")
missing_required(keys, missing_required(keys,
["loc esc", "loc tab", "backspace", "delete"], ["esc", "tab", "backspace", "delete",
"f11_placeholder", "f12_placeholder"],
"Layout doesn't define some important keys") "Layout doesn't define some important keys")
unexpected_keys(keys, unexpected_keys(keys,
["copy", "paste", "cut", "selectAll", "shareText", ["copy", "paste", "cut", "selectAll", "shareText",
"pasteAsPlainText", "undo", "redo" ], "pasteAsPlainText", "undo", "redo", "replaceText",
"textAssist", "autofill" ],
"Layout contains editing keys") "Layout contains editing keys")
unexpected_keys(keys, unexpected_keys(keys,
[ "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", [ "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9",
"f10", "f11", "f12" ], "f10", "f11", "f12" ],
"Layout contains function keys") "Layout contains function keys")
unexpected_keys(keys, [""], "Layout contains empty strings")
unexpected_keys(keys, ["loc"], "Special keyword cannot be a symbol")
unexpected_keys(keys, filter(lambda k: k.strip()!=k, keys), "Some keys contain whitespaces")
unexpected_keys(keys, ["f11_placeholder", "f12_placeholder"], "These keys are now added automatically")
_, bottom_row_keys, _ = parse_row("res/xml/bottom_row.xml") bottom_row_keys = [
"ctrl", "fn", "switch_numeric", "change_method", "switch_emoji",
"config", "switch_forward", "switch_backward", "enter", "action",
"left", "up", "right", "down", "space"
]
if root.get("bottom_row") == "false": if root.get("bottom_row") == "false":
missing_required(keys, bottom_row_keys, missing_required(keys, bottom_row_keys,
@ -102,15 +84,14 @@ def check_layout(layout):
if root.get("script") == None: if root.get("script") == None:
warn("Layout doesn't specify a script.") warn("Layout doesn't specify a script.")
for fname in sorted(sys.argv[1:]): for fname in sys.argv[1:]:
layout_id, _ = os.path.splitext(os.path.basename(fname)) if fname in KNOWN_NOT_LAYOUT:
if layout_id in KNOWN_NOT_LAYOUT:
continue continue
layout = parse_layout(fname) layout = parse_layout(fname)
if layout == None: if layout == None:
print("Not a layout file: %s" % layout_id) print("Not a layout file: %s" % fname)
else: else:
print("# %s" % layout_id) print("# %s" % fname)
warning_count = 0 warning_count = 0
check_layout(layout) check_layout(layout)
print("%d warnings" % warning_count) print("%d warnings" % warning_count)

View File

@ -1,155 +0,0 @@
# Custom layouts
You select a key layout for Unexpected Keyboard by calling up the Settings page (swipe the gear icon) and, at the top of the page, either tapping an existing layout or tapping _Add an alternate layout_. This displays a menu of available layouts. You can define your own layout by choosing _Custom layout_ at the bottom of this menu. Unexpected Keyboard now displays code in the XML format. You make changes by replacing this with different code and tapping OK.
We recommend you keep your work in a file outside Unexpected Keyboard (named something like `MyChanges.xml`). If you installed a new version of Unexpected from a different website (with a different signature), then the work you did solely by editing the XML inside Unexpected would be lost.
Put initial contents into your file in one of these ways:
* Copypaste the code Unexpected displays for _Custom layout_.
* Make a copy of one of the built-in layouts found in [`/srcs/layouts`](https://github.com/Julow/Unexpected-Keyboard/tree/master/srcs/layouts).
* Use the [web-based editor](https://unexpected-keyboard-layout-editor.lixquid.com/). Interact with this web page to define keys and swipes and move keys to desired positions, and it will write the XML code for you. You can make the web page put the XML in a text file or copy it to the clipboard.
When you have prepared suitable XML code in one of these ways, copy it to the clipboard and paste it into Unexpected Keyboard.
## XML language overview
A layout XML file comprises tags that start with `<` and end with `>`.
* Every layout file starts with this declaration:
`<?xml version="1.0" encoding="utf-8"?>`
* Certain tags come in pairs—an opening tag and a closing tag—and apply to everything between them.
* The `<keyboard>`...`</keyboard>` pair says that the material between them is the definition of your keyboard. There can be only one of these.
* The `<row>`...`</row>` pair encloses the definition of a single row.
* An optional `<modmap>`...`</modmap>` pair contains instructions if you want to change the behavior of a modifier key such as Shift.
* Stand-alone tags include `<key`...`/>`, which defines a single key.
A tag can have properties, defined using an equals sign and a pair of ASCII double quotes. For example, `<key key0="a" />` defines the "a" key. The `key0` property of the `key` tag says which key you are defining, and the tag's location inside `<row>`...`</row>` specifies where it will go in the row.
### Example
Here is a complete keyboard file with a single row containing an "a" key on the left and a "b" key on the right:
<?xml version="1.0" encoding="utf-8"?>
<keyboard name="Simple example" script="latin">
<row>
<key key0="a" />
<key key0="b" />
</row>
</keyboard>
## Keyboard metadata
The `<keyboard>`...`</keyboard>` pair follows the declaration tag and encloses the whole keyboard. The following properties may be used (The first two appear in the example above):
* `name`: The name of the keyboard. The name you specify will appear in the Settings menu. If not present, the layout will just appear as “Custom layout”.
* `script`: The (main) writing system that the keyboard supports. The possible values are `arabic`, `armenian`, `bengali`, `cyrillic`, `devanagari`, `gujarati`, `hangul`, `hebrew`, `latin`, `persian`, `shavian`, and `urdu`. It defaults to `latin`.
* `numpad_script`: The script to use for the numpad. This is useful for scripts where a different, non-ASCII set of numerals is used, like Devanagari and Arabic. It defaults to the same as `script`.
* `bottom_row`: Whether or not to show the built-in bottom row. It accepts `true` or `false`, and defaults to `true`. If your custom layout defines the bottom row, then specify `bottom_row="false"` to disable the built-in bottom row.
+ We recommend your layout use the built-in bottom row, because it is still evolving and your layout will incorporate innovations in future versions. However, to define your own, the current definition of the bottom row is in [bottom_row.xml](https://github.com/Julow/Unexpected-Keyboard/res/xml/bottom_row.xml). You can copypaste this XML into your custom layout as a starting point.
+ Likewise, the current definition of the top (number) row is in [number_row.xml](https://github.com/Julow/Unexpected-Keyboard/res/xml/number_row.xml).
* `locale_extra_keys`: Whether Unexpected should add language-dependent extra keys from [method.xml](../res/xml/method.xml) to this layout. It accepts `true` or `false`, and defaults to `true`. To disable these automatic additions, specify `locale_extra_keys="false"`.
## Row
The `<row>`...`</row>` pair encloses one row on the keyboard. It has only one optional property:
* `height`: The height of the row: a positive floating-point value.
A row's default height is 1.0 (one quarter of the keyboard height specified on the Settings menu). The `height` property makes the row taller or shorter than this. For example, if you define a 5-row keyboard but one row has `height="0.7"`, then the keyboard's total height is 4.7 units. If the total is different from 4.0, the keyboard will be taller or shorter than that specified in Settings.
## Key
The `<key />` tag defines a key on the keyboard. Its position in the sequence of keys inside `<row>`...`</row>` indicates its position in the row from left to right. What the key does is defined by optional properties.
### Taps
What the key does when tapped is defined by the optional `key0` property. For example, `<key key0="a" />` defines the "a" key. Unexpected Keyboard provides a legend in the middle of the key.
When the Shift modifier is tapped, the "a" key becomes the "A" key and the legend temporarily changes. The Fn modifier makes a different change. You can override this behavior with a modmap (see below).
### Swipes
The following optional properties define the effects of swipes:
* `n`, `ne`, `e`, `se`, `s`, `sw`, `w`, `nw`: What the key should do when it is swiped in the direction of that compass point. ("North" means upward and "East" is to the right.)
<TABLE ALIGN=CENTER>
<TR>
<TD STYLE="width: 6em;">nw</TD><TD>n</TD><TD>ne</TD>
</TR>
<TR ALIGN=CENTER>
<TD>w</TD><TD>&nbsp;</TD><TD>e</TD>
</TR>
<TR>
<TD>sw</TD><TD>s</TD><TD>se</TD>
</TR>
</TABLE>
* `key1` through `key8` is an older way to achieve the same effects. The directions are ordered as follows:
<TABLE ALIGN=CENTER>
<TR>
<TD>key1</TD><TD>key7</TD><TD>key2</TD>
</TR>
<TR>
<TD>key5</TD><TD>&nbsp;</TD><TD>key6</TD>
</TR>
<TR>
<TD>key3</TD><TD>key8</TD><TD>key4</TD>
</TR>
</TABLE>
You can define a swipe only once with either compass-point or numeric notation. Unexpected Keyboard automatically puts a small legend in that direction from the center of the key.
* `slider`: If `slider="true"`, and the key also has `w` and `e` properties, then the key tracks horizontal finger motion precisely and sends the `w` and `e` keystrokes repeatedly. In built-in layouts, this makes the space bar send left and right characters as the user slides on the space bar.
* `anticircle`: The key value to send when doing an anti-clockwise gesture on the key.
### Layout
A key may have the following properties to control the row's layout:
* `width`: The width of the key, a positive floating-point value. It defaults to 1.0
* `shift`: How much empty space to add to the left of this key, a non-negative floating-point value. It defaults to 0.0
Normally, a key's width is 1.0 unit. Unexpected Keyboard occupies the full width of the screen, and the row defining the highest number of units (in widths plus shifts) is as wide as the screen. A row whose width is a smaller number of units has empty space on the right.
### Extra legend
* `indication`: An optional extra legend to show under the main label. For example, `<key key0="2" indication="ABC" />` displays ABC at the bottom of the 2 key, as on a pinpad or some telephones. If the key also defines a downward swipe with `s` or `key8`, the legends overlap.
### Possible key values
Built-in strings that assign a special function to a key are described in [this page](Possible-key-values.md). For example, `se="copy"` means a southeasterly swipe produces the Copy key. If a key value does not match any of the built-in strings, it outputs that text _verbatim_. For example, `key0="a"` simply outputs the letter a.
In a layout, a key value can also start with the `loc` prefix. These are place-holders; the tap or swipe does nothing unless enabled through the "Add keys to keyboard" option in the Settings menu, or implicitly enabled by the language the device is set to use. For example, `ne="loc accent_aigu"` says that a northeast swipe produces the acute accent combinatorial key—if enabled.
## Modmap
The `<modmap>`...`</modmap>` pair encloses custom mappings for modifier keys. The modmap is placed inside the `<keyboard>`...`</keyboard>` pair, but outside any row. A layout can have at most one modmap. It can contain any number of mappings. Each mapping has an `a` property and a `b` property and maps the `a` key to the `b` key. Valid values are listed in [Possible key values](Possible-key-values.md).
The following mappings are supported:
```xml
<shift a="before" b="after" />
```
This means that when the Shift modifier is on, the key `before` is changed into `after`.
```xml
<fn a="before" b="after" />
```
This means that when the Fn modifier is on, the key `before` is changed into `after`.
```xml
<ctrl a="before" b="after" />
```
This means that when the Ctrl modifier is on, the key `before` is changed into `after`. The `<ctrl />` mapping is special in that the Ctrl modifier is applied to `after` after the mapping.
The clockwise circle and the round-trip gestures are affected by both `<shift />` and `<fn />` mappings. The Shift mappings are used first and if that did not modify the key, the Fn mappings are used instead.
### Examples
① Turkish keyboards use the Latin alphabet, but when "i" is shifted, it should produce "İ". This is achieved with the following mapping:
```xml
<shift a="i" b="İ" />
```
② Cyrillic layouts have no V key. A layout can define Ctrl-V with the following mapping:
```xml
<ctrl a="в" b="v" />
```
This maps Ctrl+в to Ctrl+V—not to v.
## Portrait vs. landscape
Unexpected Keyboard remembers *separately* which layout has last been used in portrait and landscape orientation. So you may have one custom layout for portrait orientation, but another custom layout for landscape orientation, and Unexpected Keyboard will switch between them without your intervention.
## Contributing your layout
The Unexpected Keyboard project enthusiastically accepts user contributions, including custom layouts. (See the guidance for layouts at [CONTRIBUTING.md](https://github.com/Julow/Unexpected-Keyboard/blob/master/CONTRIBUTING.md#Adding-a-layout)).
* Submit a layout that has innovations of possible interest to other users at [Unexpected-Keyboard-layouts](https://github.com/Julow/Unexpected-Keyboard-layouts).
* Propose that your layout be included in the set of built-in layouts by making a Pull Request for an addition to [srcs/layouts](https://github.com/Julow/Unexpected-Keyboard/tree/master/srcs/layouts). Please show that such a layout is standard in your locale or has a substantial number of users.

View File

@ -1,190 +0,0 @@
# Key values
This is an exhaustive list of special values accepted for the `key0` through `key8` or `nw` through `se` attributes on a key.
Any string that does not exactly match these will be printed verbatim.
A key can output multiple characters, but cannot combine multiple built-in key values.
## Escape codes
Value | Escape code for
:---- | :------
`\?` | `?`
`\#` | `#`
`\@` | `@`
`\n` | Literal newline character. This is different from `enter` and `action` in certain apps.
`\t` | Literal tab character. This is different from `tab` in certain apps.
`\\` | `\`
XML escape codes also work, including:
Value | Escape code for
:------- | :------
`&amp;` | `&`
`&lt;` | `<`
`&gt;` | `>`
`&quot;` | `"`
## Modifiers
System modifiers are sent to the app, which is free to do whatever they want in response.
The other modifiers only exist within the keyboard.
Value | Meaning
:---------- | :------
`shift` | System modifier.
`ctrl` | System modifier.
`alt` | System modifier.
`meta` | System modifier. Equivalent to the Windows key.
`fn` | Activates Fn mode, which assigns letters and symbols to special characters. e.g. `fn` `!` = `¡`
`compose` | Compose key. Enables composing characters using Linux-like shortcuts; e.g. `Compose` `A` `single quote` types `Á` (A with acute accent).
`capslock` | Actives and locks Shift
## Special keys
These keys are sent to apps, which are free to ignore them. The keyboard does not perform editing in response to these keys.
`esc`, `enter`,
`up`, `right`,
`down`, `left`,
`page_up`, `page_down`,
`home`, `end`,
`backspace`, `delete`,
`insert`, `scroll_lock`,
`f1`-`f12`,
`tab`, `copy`,
`paste`, `cut`,
`selectAll`, `pasteAsPlainText`,
`undo`, `redo`
## Whitespace
Value | Meaning
:------ | :------
`space` | Space bar.
`nbsp` | Non-breaking space.
`nnbsp` | Narrow non-breaking space.
`zwj` | Zero-width joiner.
`zwnj` | Zero-width non-joiner.
## Keyboard editing actions
These keys perform editing on the text without sending keys that the app can interpret differently or ignore.
Value | Meaning
:----------------- | :------
`cursor_left` | Moves the cursor position to the left directly, without sending a `left` key event.
`cursor_right` | Moves the cursor position to the right directly, without sending a `right` key event.
## Other modifiers and diacritics
Value | Meaning
:------------------- | :------
`accent_aigu` | Acute accent. `á`
`accent_caron` | Háček. `č`
`accent_cedille` | Cedilla. `ç`
`accent_circonflexe` | Circumflex. `â`
`accent_grave` | Grave accent. `à`
`accent_macron` | Macron. `ā`
`accent_ring` | Ring accent. `å`
`accent_tilde` | Tilde. `ã`
`accent_trema` | Dieresis/umlaut. `ä`
`accent_ogonek` | Ogonek. `ą`
`accent_dot_above` | Dot accent. `ż` If applied to the lowercase `i`, removes the dot instead for Turkish. `ı`
`accent_double_aigu` | Double acute accent. `ő`
`accent_slash` | Slash through. `ø`
`accent_arrow_right` | Right arrow above, used to denote a vector. `a⃗`
`accent_breve` | Breve. `ă`
`accent_bar` | Bar/strikethrough. `ɨ`
`accent_dot_below` | Dot below. `ạ`
`accent_horn` | Horn accent. `ơ`
`accent_hook_above` | Hook accent. `ả`
`superscript` | Superscript. `ᵃ`
`subscript` | Subscript. `ₐ`
`ordinal` | Turns `a` and `o` into `ª` and `º`.
`arrows` | Turns `1`-`4` and `6`-`9` into arrows.
`box` | Turns `1`-`9`, `0`, and `.` into single-line, thin box-drawing characters.
## Bidirectional
Value | Meaning
:------ | :------
`lrm` | Left-to-right mark.
`rlm` | Right-to-left mark.
`b(`, `b)`, `b[`, `b]`, `b{`, `b}`, `blt`, `bgt` | Sends the bracket characters, but with mirrored key legends for right-to-left languages. (`blt` and `bgt` print `<` and `>` respectively.)
## Hebrew
Keys ending in `_placeholder` are normally hidden unless the Fn key is pressed.
`qamats`, `patah`,
`sheva`, `dagesh`,
`hiriq`, `segol`,
`tsere`, `holam`,
`qubuts`, `hataf_patah`,
`hataf_qamats`, `hataf_segol`,
`shindot`, `shindot_placeholder`,
`sindot`, `sindot_placeholder`,
`geresh`, `gershayim`,
`maqaf`, `rafe`,
`ole`, `ole_placeholder`,
`meteg`, `meteg_placeholder`
## Keyboard behavior keys
Value | Meaning
:--------------------- | :------
`config` | Gear icon; opens Unexpected Keyboard settings.
`switch_text` | Switch to the text layer (main layer).
`switch_numeric` | Switch to the numeric layer.
`switch_emoji` | Switch to the emoji layer.
`switch_back_emoji` | Switch to the text layer from the emoji layer.
`switch_forward` | Change the keyboard layout, as long as Unexpected Keyboard has multiple keyboard layouts enabled in the settings.
`switch_backward` | Change the keyboard layout to the previous one in the list.
`switch_greekmath` | Switch to the Greek & Math Symbols layer.
`switch_clipboard` | Switch to the clipboard pane.
`change_method` | Open the input method picker dialog.
`change_method_prev` | Switch to the previously used input method.
`action` | Performs a special context-sensitive operation related to the Enter key. For example, in the Twitter (X) app, `enter` adds a new line, while `action` posts.
`voice_typing` | Begin voice typing.
`voice_typing_chooser` | Shows a menu where you can choose which voice typing provider to use, then begins voice typing when you make a selection.
`shareText` | Emit a share Intent for the selected text. **Oddity:** This is in CamelCase.
## Unused
These keys are known to do nothing.
`replaceText`, `textAssist`,
`autofill`, `removed`
## Placeholders
These keys are normally hidden unless the Fn modifier is activated.
`f11_placeholder` | `f12_placeholder`
## Complex keys
More complex keys are of this form:
```
:<kind> <attributes>:<payload>
```
Where `<kind>` is one of the kinds documented below and `<attributes>` is a
space separated list of attributes. `<payload>` depends on the `<kind>`.
Attributes are:
- `symbol='Sym'` specifies the symbol to be shown on the keyboard.
- `flags='<flags>'` changes the behavior of the key.
`<flags>` is a coma separated list of:
+ `dim`: Make the symbol dimmer.
+ `small`: Make the symbol smaller.
### Kind `str`
Defines a key that outputs an arbitrary string. `<payload>` is a string wrapped
in single-quotes (`'`), escaping of other single quotes is allowed with `\'`.
For example:
- `:str:'Arbitrary string with a \' inside'`
- `:str symbol='Symbol':'Output string'`
### Kind `char`
Defines a key that outputs a single character. `<payload>` is the character to
output, unquoted.
This kind of key can be used to define a character key with a different symbol
on it. `char` keys can be modified by `ctrl` and other modifiers, unlike `str`
keys.
For example:
- `:char symbol='q':љ`, which is used to implement `ctrl` shortcuts in cyrillic
layouts.

View File

@ -1,6 +0,0 @@
Hlavní funkcí je možnost psát více znaků posunutím kláves směrem k rohům.
Tato aplikace byla původně navržena pro programátory používající Termux.
Nyní je ideální pro každodenní použití.
Tato aplikace neobsahuje žádné reklamy, nevyužívá připojení k síti a je Open Source.

View File

@ -1,9 +0,0 @@
Allow selecting any number of standard and custom layouts.
Allow adding custom keys to the keyboard.
Changed behavior of auto-added keys (often dead-keys).
New layouts.
Improved layouts and language support.
Improved the space bar slider, and many more.
Updated translations.
Thanks to the contributors: @ChasmSolacer, @ElucGeek, @GoRaN909, @RZHSSNZDH, @Shareef101, @Validbit, @eandersons, @nitsvga, @polyctena, @sdrapha, @syskill

View File

@ -1,8 +0,0 @@
Improved custom layout option.
Allow selecting voice typing app with a long press.
The numpad can work with other numeral systems.
New and updated layouts.
New themes.
Many small improvements.
Thanks to the contributors: @pharook, @syskill, @ojas-bhagavath, @lrvideckis, @lyubomirv, @matthiakl, @deftkHD, @V6lhost, @RZHSSNZDH, @RetrogisusDEV, @rafasaurus, @krtsgnr7230, @eandersons, @ChasmSolacer, @Validbit, @polyctena

View File

@ -1,7 +0,0 @@
The custom vibration setting is back.
Allow to hide the keyboard switching key.
Fixed modifier keys in some development apps.
Updated translations.
Bug fixes and general improvements.
Many thanks to the contributors: @abb128, @marciozomb13, @RetrogisusDEV, @Sestowner, @vedamanavi, @krtsgnr7230

View File

@ -1,6 +0,0 @@
New layouts: QWERTY (Slovak), Gujarati phonetic
Add Linux Compose key to type a variety of characters using known combinations.
Fixed localized numpad and number row.
Many improvements and bug fixes.
Huge thanks to the contributors: @Yogesh-B, @ChasmSolacer, @matthiakl, @Sestowner, @RyanGibb, @BogdanLata, @RetrogisusDEV, @V6lhost, @ErrrorMaxx, @sdrapha, @vedamanavi

View File

@ -1,11 +0,0 @@
MessagEase and Thumb-Key inspired gestures with the anti-clockwise circle being
completely configurable.
Updated emojis.
Improved Hangul (Korean) support.
Added Danish support.
Improved spacebar slider.
Updated translations.
Various improvements and fixes to the app and layouts.
Many thanks to the contributors: @alotbsol555 @bkmgit @Cheesebaron @CloneWith @eandersons @JapanYoshi @Julow @la-ninpre @m15a @marciozomb13 @polyctena @solokot @Spike-from-NH @tmqCypher @Validbit

View File

@ -1,8 +0,0 @@
Clipboard pane
New Monet theme
Improvements to custom layouts
Options to disable key repeat and the circle gesture
Options to disable the Tab and Esc keys
Many thanks to the contributors: @alotbsol555 @ChasmSolacer @eandersons @polyctena @Sestowner @solokot @Spike-from-NH @TadaCZE @V6lhost @Validbit

View File

@ -1,6 +0,0 @@
Fix crash on Android 6.
Fix scaling issues for layouts with few columns.
Added Serbian layouts.
Layout and compose key tweaks. Improvements to custom layouts. Added Menu key.
Many thanks to the contributors: @adrian4096 @asivery @bokidori @dingodoppelt @RZHSSNZDH

View File

@ -1 +0,0 @@
https://www.youtube.com/watch?v=rwGvWesPFX8

View File

@ -1,7 +0,0 @@
このキーボードは、キーの角をスワイプすることで様々なキーを入力できます。
このアプリは元々はTermuxでのプログラミング用に設計されました。
しかし、今では普段の入力にも適しています。
PCキーボードでの半角入力を再現しています。日本語入力、変換は出来ません。
このアプリは広告を含まず、インターネットに接続せず、そしてオープンソースです。

View File

@ -1 +0,0 @@
軽量でプライバシーに配慮したAndroid用仮想キーボード

View File

@ -1,6 +0,0 @@
주요 기능은 모서리 방향으로 키를 스와이프하여 더 많은 문자를 입력할 수 있다는 것입니다.
이 앱은 처음에는 Termux를 사용하는 프로그래머들을 위한 것으로 개발되었습니다.
지금은 일상적인 용도로도 완벽합니다.
이 응용 프로그램에는 광고가 없으며 네트워크 요청을 하지 않고 오픈 소스입니다.

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,6 +0,0 @@
A principal característica é que você pode digitar mais caracteres deslizando as teclas para os cantos.
O app foi criado originalmente para desenvolvedores que usam Termux.
Agora aperfeiçoado para o uso diário.
Este aplicativo não contém anúncios, não faz nenhuma solicitação de rede e é Open Source.

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,6 +0,0 @@
Главная особенность клавиатуры — это возможность легко напечатать любой ASCII-символ жестами в углы клавиш.
Приложение изначально было разработано для использования с Termux.
На данный момент оно также удобно в повседневном использовании.
Приложение не содержит рекламы, не осуществляет никаких запросов в сеть и имеет открытый исходный код.

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,6 +0,0 @@
Bu uygulama özünde tuşların kenarlarından kaydırarak daha fazla karakter yazabilmek amacıyla geliştirildi.
Bu uygulama aslında Termux kullanıcıları için geliştirildi.
Artık gündelik kullanım için de uygun.
Bu uygulama açık kaynaklıdır. Reklam içermez ve internete bağlanmaz.

View File

@ -1 +0,0 @@
Android için hafif ve güvenlik odaklı bir sanal klavye uygulaması.

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,6 +0,0 @@
Головна особливість полягає в тому, що ви можете вводити більше символів, проводячи клавіші до кутів.
Ця програма спочатку була розроблена для програмістів, які використовують Termux.
Тепер ідеально підходить для щоденного використання.
Ця програма не містить реклами, не надсилає жодних мережевих запитів і має відкритий код.

View File

@ -1 +0,0 @@
Легка та конфіденційна віртуальна клавіатура для Android.

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,6 +0,0 @@
此应用的主要功能是,通过将按键沿四角滑动,您可以输入更多字符。
此应用最初是为使用 Termux 的程序员而设计的。
现在对于日常使用来说也很完美。
此应用没有广告,不会发送任何网络请求,而且是开源的。

View File

@ -1 +0,0 @@
适用于 Android 的轻量级、注重隐私的虚拟键盘。

View File

@ -1 +0,0 @@
Unexpected Keyboard

View File

@ -1,38 +0,0 @@
import urllib.request
import os.path
EMOJIS_PATH = 'res/raw/emojis.txt'
EMOJI_TEST_PATH = 'emoji-test.txt'
EMOJI_TEST_URL = 'https://unicode.org/Public/emoji/latest/emoji-test.txt'
def rawEmojiFromCodes(codes):
return ''.join([chr(int(c, 16)) for c in codes])
def getEmojiTestContents():
if os.path.exists(EMOJI_TEST_PATH):
print(f'Using existing {EMOJI_TEST_PATH}')
else:
print(f'Downloading {EMOJI_TEST_URL}')
urllib.request.urlretrieve(EMOJI_TEST_URL, EMOJI_TEST_PATH)
return open(EMOJI_TEST_PATH, mode='r', encoding='UTF-8').read()
emoji_list = []
group_indices = []
for line in getEmojiTestContents().splitlines():
if line.startswith('# group:'):
if len(group_indices) == 0 or len(emoji_list) > group_indices[-1]:
group_indices.append(len(emoji_list))
elif not line.startswith('#') and 'fully-qualified' in line:
codes = line.split(';')[0].split()
emoji_list.append(rawEmojiFromCodes(codes))
with open(EMOJIS_PATH, 'w', encoding='UTF-8') as emojis:
for e in emoji_list:
emojis.write(f'{e}\n')
emojis.write('\n')
emojis.write(' '.join([str(g) for g in group_indices]))
emojis.write('\n')
print(f'Parsed {len(emoji_list)} emojis in {len(group_indices)}')

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# Generates the list of layouts in res/values/layouts.xml from the layout files # Generates the list of layouts in res/values/layouts.xml from the layout files
# in srcs/layouts. Every layouts must have a 'name' attribute to be listed. # in res/xml. Every layouts must have a 'name' attribute to be listed.
import itertools as it import itertools as it
import sys, os, glob import sys, os, glob
@ -11,6 +11,11 @@ import xml.etree.ElementTree as XML
# are sorted alphabetically. # are sorted alphabetically.
FIRST_LAYOUTS = [ "latn_qwerty_us", "latn_colemak", "latn_dvorak" ] FIRST_LAYOUTS = [ "latn_qwerty_us", "latn_colemak", "latn_dvorak" ]
# File names that are known not to be layouts. Avoid warning about them.
KNOWN_NOT_LAYOUT = set([
"number_row", "numpad", "pin", "bottom_row", "settings", "method",
"greekmath", "numeric" ])
# Read a layout from a file. Returns [None] if [fname] is not a layout. # Read a layout from a file. Returns [None] if [fname] is not a layout.
def read_layout(fname): def read_layout(fname):
root = XML.parse(fname).getroot() root = XML.parse(fname).getroot()
@ -23,7 +28,9 @@ def read_layouts(files):
for layout_file in files: for layout_file in files:
layout_id, _ = os.path.splitext(os.path.basename(layout_file)) layout_id, _ = os.path.splitext(os.path.basename(layout_file))
layout = read_layout(layout_file) layout = read_layout(layout_file)
if layout == None: if layout_id in KNOWN_NOT_LAYOUT:
continue
elif layout == None:
print("Not a layout file: %s" % layout_file) print("Not a layout file: %s" % layout_file)
elif layout["name"] == None: elif layout["name"] == None:
print("Layout doesn't have a name: %s" % layout_id) print("Layout doesn't have a name: %s" % layout_id)
@ -47,9 +54,9 @@ def generate_arrays(out, layouts):
item.text = s item.text = s
elem.append(item) elem.append(item)
return elem return elem
system_item = [ ("system", "@string/pref_layout_e_system") ] none_item = [ ("system", "@string/pref_layout_e_system") ]
custom_item = [ ("custom", "@string/pref_layout_e_custom") ] custom_item = [ ("custom", "@string/pref_layout_e_custom") ]
values_items, entries_items = zip(*(system_item + layouts + custom_item)) # unzip values_items, entries_items = zip(*(none_item + layouts + custom_item)) # unzip
ids_items = map(lambda s: "@xml/%s" % s if s not in ["system", "custom"] else "-1", values_items) ids_items = map(lambda s: "@xml/%s" % s if s not in ["system", "custom"] else "-1", values_items)
root = XML.Element("resources") root = XML.Element("resources")
root.append(XML.Comment(text="DO NOT EDIT. This file is generated, see gen_layouts.py.")) root.append(XML.Comment(text="DO NOT EDIT. This file is generated, see gen_layouts.py."))
@ -59,6 +66,6 @@ def generate_arrays(out, layouts):
XML.indent(root) XML.indent(root)
XML.ElementTree(element=root).write(out, encoding="unicode", xml_declaration=True) XML.ElementTree(element=root).write(out, encoding="unicode", xml_declaration=True)
layouts = sort_layouts(read_layouts(glob.glob("srcs/layouts/*.xml"))) layouts = sort_layouts(read_layouts(glob.glob("res/xml/*.xml")))
with open("res/values/layouts.xml", "w") as out: with open("res/values/layouts.xml", "w") as out:
generate_arrays(out, layouts) generate_arrays(out, layouts)

View File

@ -1,3 +0,0 @@
org.gradle.jvmargs=-Dfile.encoding=UTF-8
android.useAndroidX=false
android.nonTransitiveRClass=true

Binary file not shown.

View File

@ -1,6 +0,0 @@
#Mon Aug 21 18:13:41 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored
View File

@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored
View File

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,18 @@
Tato aplikace je virtuální klávesnící pro Android. Umožňuje rychlejší a plynulejší psaní písmen i symbolů (vč. diakritiky), a to ve vícero jazycích a vlastních rozloženích. To vše zdarma, bez reklam a bez plýtvání vašeho uložiště. Můžete psát libovolné znaky pomocí gest (ASCII i Unicode), používat mrtvé (univerzální) klávesy pro diakritická znaménka a mnohem více.
Klávesnice zobrazuje až 4 další znaky v rozích každé klávesy. Tyto znaky jsou vyvolány přejetím prstu do vybraného rohu.
No zkrátka...:
- Obsahuje každý znak a speciální klávesu, která je běžnou součástí počítačové klávesnice. To přijde vhod obzvláště při používání aplikací jako např. Termux
- Můžete používat modifikační klávesy, obohaceny o speciální klávesy (např. Tab, Esc, šipky, F klávesy, ale také Ctrl nebo Alt !)
- Můžete psát vícero jazyky rychleji a bez chyb. Diakritická znaménka mohou být vyvolána i za pomocí mrtvých kláves. Nejdříve zvolte diakritické znaménko a pak znak, který chcete obohatit o toto znaménko.
- Je vysoce nenáročná a rychlá. Zabere 500x méně místa než klávesnice od Googlu (Gboard) a 15x méně než výchozí klávesnice systému. Bez reklam, bez sledování.
- Má vícero rozložení: QWERTY, QWERTZ, AZERTY. Motivy: Bílá, Tmavá, OLED Černá. A mnoho dalších které si s drobnou znalostí programování můžete upravovat dle libosti.
Nezapomeňte... Jako každá virtuální klávesnice, i tato musí být aktivována v nastavení systému (zařízení). Otevřte (Systémové) Nastavení a přejděte na:
(Další nastavení NEBO Nastavení systému) > Jazyk & způsob zadávání > Spravovat klávesnice.

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Some files were not shown because too many files have changed in this diff Show More