Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cda0662ae | ||
|
|
11cacb25c8 | ||
|
|
c0207fd84e | ||
|
|
56d3acfc67 | ||
|
|
a3e5ae9337 | ||
|
|
7e84f71464 | ||
|
|
eb88fbc981 | ||
|
|
96320e6b06 | ||
|
|
fee9c2a0ac | ||
|
|
c74a5841ec | ||
|
|
aab7a6e33a | ||
|
|
0ea59cf2ed | ||
|
|
1be6ce1ae8 | ||
|
|
8d06bea6bb | ||
|
|
4b1a0c9972 | ||
|
|
3d50ea59af | ||
|
|
83bef3aee9 | ||
|
|
c365acb800 | ||
|
|
ef075151fb | ||
|
|
5316e46ba6 | ||
|
|
467fb62067 | ||
|
|
75c59366a9 | ||
|
|
6df6e25b15 | ||
|
|
81d89cad8c | ||
|
|
99954237f7 | ||
|
|
b5e56642fc | ||
|
|
343525f259 | ||
|
|
a5adad714a | ||
|
|
67214cbdf1 | ||
|
|
3a0284f355 | ||
|
|
eecada3118 | ||
|
|
cdc8635793 | ||
|
|
0ed3e7bd22 | ||
|
|
18ac2dc0d8 | ||
|
|
c04ca29421 | ||
|
|
b6466daebb | ||
|
|
d9dedc447f | ||
|
|
5dbd98ae9e | ||
|
|
f8db145a63 | ||
|
|
563a24b7d1 | ||
|
|
5dbea21fab | ||
|
|
ce937c3f58 | ||
|
|
439fdade51 | ||
|
|
a77531e483 | ||
|
|
343646f0f8 | ||
|
|
91c2337633 | ||
|
|
000c0f1e30 | ||
|
|
2401b1c776 | ||
|
|
555e329447 | ||
|
|
a2b3033d04 | ||
|
|
1020fff6cd | ||
|
|
6fa4fadf04 | ||
|
|
f386428acd | ||
|
|
f45540eab1 | ||
|
|
0d509f8cfb | ||
|
|
68061f1aac | ||
|
|
c4ac4abd63 | ||
|
|
5fd26affc9 | ||
|
|
198ae1fc7c | ||
|
|
1e9ce7ba54 | ||
|
|
702785b7fc | ||
|
|
8c37c6188b | ||
|
|
69ad3e2352 | ||
|
|
9161d1574d | ||
|
|
7ff7309e76 | ||
|
|
6df803c239 | ||
|
|
3385fe7cbd | ||
|
|
320b9e0751 | ||
|
|
9178207653 | ||
|
|
90b0812ae4 | ||
|
|
fb03a82e45 | ||
|
|
f5a7220ba7 | ||
|
|
e413f3918e | ||
|
|
d25bdd8938 | ||
|
|
1f84d08fa9 | ||
|
|
da9d68dd3b | ||
|
|
1bc36ceec7 | ||
|
|
c57b60d00c | ||
|
|
e304fbd120 | ||
|
|
7eb7c21e13 | ||
|
|
710e7ca85e | ||
|
|
207845d46f | ||
|
|
707d54b6f4 | ||
|
|
c01f167d49 | ||
|
|
176ca00f66 | ||
|
|
081cfdb0ee | ||
|
|
d9f94aecac | ||
|
|
6691706aed | ||
|
|
1b77138798 | ||
|
|
07ebd04052 | ||
|
|
40c2bfd819 | ||
|
|
855ad47674 | ||
|
|
e032e4acb8 | ||
|
|
fe6930fb76 | ||
|
|
6a10f0a01a | ||
|
|
30717eeb90 | ||
|
|
a664ab18c9 | ||
|
|
f50983d7ab | ||
|
|
be858802c5 | ||
|
|
1ba690e53a | ||
|
|
e16f81d350 | ||
|
|
0de2039d72 | ||
|
|
50b6a63468 | ||
|
|
8cb644b418 | ||
|
|
f138124670 | ||
|
|
0f76d7f9df | ||
|
|
27b9ec4628 | ||
|
|
ac733ed1dc | ||
|
|
6d15708f95 | ||
|
|
4377f3e41c | ||
|
|
1e690018d7 | ||
|
|
93bb5d2714 | ||
|
|
ad2b08a342 | ||
|
|
9e6508cee4 | ||
|
|
f735c138fb | ||
|
|
d663947fec | ||
|
|
c800617e26 | ||
|
|
f47c7abaf3 | ||
|
|
faf06ee234 | ||
|
|
07c41f9c27 | ||
|
|
80a0d9edab | ||
|
|
cd943a9d4a | ||
|
|
c3d3107b12 | ||
|
|
b91fac8e76 | ||
|
|
e2c784f4cf | ||
|
|
f83bdd8a28 | ||
|
|
dc10a459ca | ||
|
|
4bea68f151 | ||
|
|
daa8ce71ac | ||
|
|
f06f475e89 | ||
|
|
b784d0805c | ||
|
|
c245c6a37c | ||
|
|
264a287171 | ||
|
|
82d82466c6 | ||
|
|
0242d24cd1 | ||
|
|
76e683bfec | ||
|
|
ee1988d98e | ||
|
|
fe5f0d18ac | ||
|
|
41527e4f23 | ||
|
|
66fb1c5873 | ||
|
|
05103214dd | ||
|
|
bf9e2e4438 | ||
|
|
4209bdcfbe | ||
|
|
31db482bb4 | ||
|
|
e33499dab5 | ||
|
|
92b99ff34e | ||
|
|
f991c6479b | ||
|
|
5a45b1600a | ||
|
|
79f884b2a0 | ||
|
|
22330ad67b | ||
|
|
7f50a5aa77 | ||
|
|
de389918be | ||
|
|
4a57829105 | ||
|
|
bc6ca8c7fc | ||
|
|
0ffe0c915e | ||
|
|
392699f333 | ||
|
|
cf801c02fd | ||
|
|
665356f77b | ||
|
|
48c356a569 | ||
|
|
60eb92e92a | ||
|
|
602ffc2a93 | ||
|
|
dbacc0e466 | ||
|
|
1307f401cc | ||
|
|
ca6006767b | ||
|
|
2202db53ba | ||
|
|
321f19272e | ||
|
|
06a8a04020 | ||
|
|
2a1f7c3217 | ||
|
|
76952d55fe | ||
|
|
1f560f8b6b | ||
|
|
33bdc52354 | ||
|
|
97b795aed0 | ||
|
|
bb44362701 | ||
|
|
bab20c5baa | ||
|
|
a3000fe111 | ||
|
|
d4d2f52683 | ||
|
|
10ef340559 | ||
|
|
5b77262186 | ||
|
|
8ce56b1bf9 | ||
|
|
94667e8363 | ||
|
|
970b5eb82a | ||
|
|
a2ceed4521 | ||
|
|
6d7825e129 | ||
|
|
10c1a82995 | ||
|
|
267a39e870 | ||
|
|
f6fcbbcc34 | ||
|
|
f98b3cec4b | ||
|
|
e5a942be9f | ||
|
|
edc63aa680 | ||
|
|
23def145b2 | ||
|
|
3f7bd4f65d | ||
|
|
7b91d4f9d3 | ||
|
|
175369f7d7 | ||
|
|
79c5acc007 | ||
|
|
94d470dd96 | ||
|
|
ee9d61ad1e | ||
|
|
a3c7b538d0 | ||
|
|
ca4cd38bb2 | ||
|
|
7046c500ff | ||
|
|
0374a82f99 | ||
|
|
217acbd6f1 | ||
|
|
ef27d511be | ||
|
|
f9a4ffa5eb | ||
|
|
5533badd19 | ||
|
|
0f1b4b081d | ||
|
|
3feae09df0 | ||
|
|
34bb28d1fc | ||
|
|
551a294b05 | ||
|
|
671ff1d8b4 | ||
|
|
15caf66370 | ||
|
|
ae0a8e551b | ||
|
|
cb4bedfc2c | ||
|
|
7d63a6885c | ||
|
|
841d797b7c | ||
|
|
0c9ba5326a | ||
|
|
7c5a7dc148 | ||
|
|
37fc714729 | ||
|
|
ec7d65ebc0 | ||
|
|
5670af16d6 | ||
|
|
6b39a846e6 |
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,34 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help fix a bug
|
||||
about: Create a report to help FlorisBoard improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Short description of bug
|
||||
A short but clear and concise description of what the bug is.
|
||||
<!--
|
||||
- Describe the bug in a short but concise way.
|
||||
- If you have a screenshot or screen recording of the bug, link them at
|
||||
the end of this issue.
|
||||
- Please search existing bug reports to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
|
||||
#### Steps to reproduce
|
||||
**Environment information**
|
||||
- FlorisBoard Version: <!-- e.g. 0.1.0 -->
|
||||
- Install Source: <!-- Google PlayStore/F-Droid/GitHub/? -->
|
||||
- Device: <!-- e.g. OnePlus 7T -->
|
||||
- Android version, ROM: <!-- e.g. 10, Stock -->
|
||||
|
||||
**Steps to reproduce**
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
#### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
#### What happened instead?
|
||||
A detailed description of what you expected to happen. If you have screenshots or a screen recording, add it here.
|
||||
|
||||
#### Additional info
|
||||
- Version: [e.g. 0.1.0]
|
||||
- Source: [e.g. Google PlayStore/F-Droid/GitHub/?]
|
||||
- Device: [e.g. OnePlus 7T]
|
||||
- Android version, ROM: [e.g. 10, Stock]
|
||||
|
||||
#### Log
|
||||
<!-- (remove this line if you paste a log)
|
||||
```
|
||||
If applicable, paste the captured debug log here.
|
||||
```
|
||||
(remove this line if you paste a log) -->
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: General feedback
|
||||
url: https://github.com/florisboard/florisboard/blob/master/CONTRIBUTING.md
|
||||
about: Give general feedback about this project
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,11 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea or enhancement for this project
|
||||
name: Feature request / Suggestion
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: proposal
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Describe your idea in a short but concise way. If you have multiple ideas which are not directly connected to each other, file an issue per idea. This makes it easy to implement one feature proposal at a time. If you have any examples, e.g. screenshots or other keyboards which have the proposed feature implemented, link them here.
|
||||
Thank you for your help in making FlorisBoard better!
|
||||
<!--
|
||||
- Describe your idea in a short but concise way.
|
||||
- If you have multiple ideas which are not directly connected to each
|
||||
other, file an issue per idea. This makes it easy to implement one
|
||||
feature proposal at a time.
|
||||
- If you have any examples, e.g. screenshots or other keyboards which
|
||||
have the proposed feature implemented, link them here.
|
||||
- Please search existing proposals to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
|
||||
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask here if you have a question about FlorisBoard
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
- If you need assistance in using FlorisBoard, ask it here!
|
||||
- If you want to suggest an idea for this project, please use the
|
||||
Feature request template instead.
|
||||
- Please search existing questions to avoid creating duplicates.
|
||||
- Thank you for your help in making FlorisBoard better!
|
||||
-->
|
||||
32
.github/workflows/android.yml
vendored
Normal file
32
.github/workflows/android.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: FlorisBoard CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assemble
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-debug.apk
|
||||
path: app/build/outputs/apk/debug/app-debug.apk
|
||||
100
CONTRIBUTING.md
100
CONTRIBUTING.md
@@ -2,62 +2,59 @@
|
||||
|
||||
First off, thanks for considering contributing to FlorisBoard!
|
||||
|
||||
There are several ways to contribute to FlorisBoard. This document provides some
|
||||
general guidelines for each type of contribution.
|
||||
There are several ways to contribute to FlorisBoard. This document
|
||||
provides some general guidelines for each type of contribution.
|
||||
|
||||
## Giving general feedback
|
||||
|
||||
Either use the review function within Google Play or email me at
|
||||
[florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev). I
|
||||
love to hear from you!
|
||||
|
||||
## Translations
|
||||
|
||||
To make FlorisBoard accessible in as many languages as possible, the
|
||||
platform [Crowdin](https://crowdin.florisboard.patrickgold.dev) is used
|
||||
to crowdsource and manage translations. This is the only source of
|
||||
translations from now on - **PRs that add/update translations are no
|
||||
longer accepted.** The list of languages in Crowdin covers the top 20
|
||||
languages, but feel free to email me at
|
||||
[florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to
|
||||
request a language and I'll add it.
|
||||
|
||||
## Adding a new feature or making large changes
|
||||
|
||||
If you intend to add a new feature or to make large changes, please discuss this
|
||||
first through a proposal on GitHub. Discussing your idea enables both you and the
|
||||
dev team that we are on the same page before you start on working on your change.
|
||||
If you have any questions, feel free to ask for help at any time!
|
||||
If you intend to add a new feature or to make large changes, please
|
||||
discuss this first through a proposal on GitHub. Discussing your idea
|
||||
enables both you and the dev team that we are on the same page before
|
||||
you start on working on your change. If you have any questions, feel
|
||||
free to ask for help at any time!
|
||||
|
||||
## Adding a new keyboard layout / dictionary for locale
|
||||
|
||||
As FlorisBoard is currently in alpha stage, things might change drastically. This
|
||||
also includes the config scheme of keyboard layouts. To prevent incompatible
|
||||
configs because some features and structures may change, please do not add this
|
||||
kind of content yet. As FlorisBoard's state progresses and its core stabilizes,
|
||||
you will be able to add keyboard layouts.
|
||||
You can now oficially add layouts to FlorisBoard as described below.
|
||||
FlorisBoard's core has stabilized enough that adding new content is
|
||||
safe, although there will be some changes in the future.
|
||||
|
||||
## Translating FlorisBoard
|
||||
Currently you need to modify `app/src/main/assets/ime/config.json` to
|
||||
add the filename of the language/layout to the `characterLayouts`
|
||||
section and the `defaultSubtypes` section, making sure to include
|
||||
the language's IETF BCP 47 code ([ISO 639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
|
||||
and [ISO 3166-1 region code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)).
|
||||
For example, Dutch as spoken in Belgium is `nl-be`. Use a unique value
|
||||
for `id` to avoid possible crahses caused by duplicate ids.
|
||||
|
||||
Before starting to translate, when adding a new translation please file
|
||||
an issue stating that you want to translate FlorisBoard into a language.
|
||||
Once this gets approved you can start translating. When updating an
|
||||
already existing translation file you can just send a PR directly.
|
||||
Add the keyboard layout at `app/src/main/assets/ime/text/characters/<preferredLayout_name_here>.json`,
|
||||
with `code` referring to the characters codepoint and `label` being the
|
||||
respective unicode character.
|
||||
|
||||
If you are not familiar with PRs, check out this guide:
|
||||
[https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request](https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request)
|
||||
|
||||
Notes for tips below:
|
||||
- Replace `<language>` with the language you want to add
|
||||
- Replace `<code>` with the ISO 639-1 code of the language you want to
|
||||
add
|
||||
([List of codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes))
|
||||
|
||||
### Tips when adding a new translation
|
||||
|
||||
- To add the new translation file, navigate to `app/src/main/res/values`
|
||||
and copy the file `strings.xml` into the folder
|
||||
`app/src/main/res/values-<code>` (you have to create this folder)
|
||||
- Translate only the phrases inside the brackets, leave the name
|
||||
attribute as it is
|
||||
E.g.: `<string name="hello_string">Hello World!</string>`
|
||||
`<string name="hello_string">Ciao mondo!</string>`
|
||||
- When finished translating, commit your changes locally, as the commit
|
||||
message use `Add <language> translation`
|
||||
- Push your change(s) and create the PR. When everything checks out, it
|
||||
will get accepted.
|
||||
|
||||
### Tips when updating a translation
|
||||
- To update a translation, check the `strings.xml` in
|
||||
`app/src/main/res/values` for newly added strings and add them to the
|
||||
translation file in `app/src/main/res/values-<code>`
|
||||
- When finished translating, commit your changes locally, as the commit
|
||||
message use `Update <language> translation`
|
||||
- Push your change(s) and create the PR. When everything checks out, it
|
||||
will get accepted.
|
||||
Any accents or diacritics that should be exposed via long press can be
|
||||
added at `assets/ime/text/characters/extended_popups/<languageTag_name_here>.json`.
|
||||
For each key, you can add 1 main and several relevant accents. The main
|
||||
accent should be used for accents which are important for the language
|
||||
you add. The main field is used for determining if a hint or an accent
|
||||
should take priority, so please make sure to leave main empty and just
|
||||
use relevant for accents which are not-so important.
|
||||
|
||||
## Bug reporting
|
||||
|
||||
@@ -68,6 +65,11 @@ use the premade [issue template](.github/ISSUE_TEMPLATE/bug_report.md)
|
||||
for bug reporting. This makes it easy for us to understand what the bug
|
||||
is and how to solve it.
|
||||
|
||||
### Capturing ADB debug logs
|
||||
### Capturing error logs
|
||||
|
||||
[[ TODO: create tutorial ]]
|
||||
Logs are captured by FlorisBoard's crash handler, which gives you the
|
||||
ability to copy it to the clipboard and paste it in GitHub. This is the
|
||||
preferred way to capture logs.
|
||||
|
||||
Alternatively, you can also use ADB (Android Debug Bridge) to capture
|
||||
the error log. This is recommended for experienced users only.
|
||||
|
||||
158
README.md
158
README.md
@@ -1,10 +1,23 @@
|
||||
# FlorisBoard
|
||||
<img align="left" width="80" height="80"
|
||||
src="fastlane/metadata/android/en-US/images/icon.png" alt="App icon">
|
||||
|
||||
An open-source keyboard for Android. Currently in alpha stage.
|
||||
# FlorisBoard [](https://github.com/florisboard/florisboard/releases) [](https://crowdin.florisboard.patrickgold.dev) 
|
||||
|
||||
#### Public Alpha Test Programme
|
||||
Wanna try it out on your device? You can join the public alpha test
|
||||
programme on Google Play. To become a tester, follow these steps:
|
||||
**FlorisBoard** is a free and open-source keyboard for Android 6.0+
|
||||
devices. It aims at being modern, user-friendly and customizable while
|
||||
fully respecting your privacy. Currently in alpha/early-beta state.
|
||||
|
||||
## Public Alpha Test Programme
|
||||
Wanna try it out on your device? Use one of the following options:
|
||||
|
||||
_A. Get it on F-Droid_:
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="64" alt="F-Droid badge">](https://f-droid.org/packages/dev.patrickgold.florisboard)
|
||||
|
||||
_B. Google Play Public Alpha Test_:
|
||||
|
||||
You can join the public alpha test programme on Google Play. To become a
|
||||
tester, follow these steps:
|
||||
1. Join the
|
||||
[FlorisBoard Public Alpha Test](https://groups.google.com/g/florisboard-public-alpha-test)
|
||||
Google Group to be able to access the testing programme.
|
||||
@@ -18,84 +31,139 @@ programme on Google Play. To become a tester, follow these steps:
|
||||
4. Finished! You will receive future versions of FlorisBoard via Google
|
||||
Play.
|
||||
|
||||
##### Giving feedback
|
||||
If you want to give feedback to FlorisBoard, there are 2 ways to do so,
|
||||
as listed below:
|
||||
- *General feedback:* use the private feedback to developer section on
|
||||
the PlayStore listing.
|
||||
- *Bug reports or feature requests:* see the
|
||||
[contribution guidelines](CONTRIBUTING.md)
|
||||
With the v0.4.0 release FlorisBoard will enter the public beta in GPlay, allowing to directly search
|
||||
for and download FlorisBoard without prior joining the alpha group.
|
||||
|
||||
Thank you for contributing to FlorisBoard!
|
||||
_C. Use the APK provided in the release section of this repo_
|
||||
|
||||
##### Note on F-Droid release
|
||||
FlorisBoard is currently only available through Google Play, but it is
|
||||
planned to also release it via F-Droid later on. There is no exact
|
||||
timeline for this, but I aim for the 0.2.0 or 0.3.0 release.
|
||||
### Giving feedback
|
||||
If you want to give feedback to FlorisBoard, there are several ways to
|
||||
do so, as listed [here](CONTRIBUTING.md#giving-general-feedback).
|
||||
|
||||
---
|
||||
|
||||

|
||||
<img align="right" height="256"
|
||||
src="https://patrickgold.dev/media/previews/florisboard-preview-day.png"
|
||||
alt="Preview image">
|
||||
|
||||
## Feature roadmap
|
||||
## Implemented features
|
||||
This list contains all implemented and fully functional features
|
||||
FlorisBoard currently has to offer. For planned features and its
|
||||
milestones, please refer to the [Feature roadmap](#feature-roadmap).
|
||||
|
||||
### Basics
|
||||
* [x] Implementation of the keyboard core (InputMethodService)
|
||||
* [x] Own implementation of deprecated KeyboardView (base only)
|
||||
* [x] Custom implementation of deprecated KeyboardView (base only)
|
||||
* [x] Caps + Caps Lock
|
||||
* [x] Key popups
|
||||
* [x] Extended key popups (e.g. a -> á, à, ä, ...) (needs tweaks for
|
||||
emojis)
|
||||
* [x] Extended key popups (e.g. a -> á, à, ä, ...)
|
||||
* [x] Key press sound/vibration
|
||||
* [x] Portrait orientation support
|
||||
* [x] Landscape orientation support (needs tweaks)
|
||||
* [ ] Tablet screen support
|
||||
|
||||
### Layouts
|
||||
* [x] Latin character layouts (QWERTY, QWERTZ, AZERTY, Swiss, Spanish,
|
||||
Norwegian, Swedish/Finnish, Icelandic, Danish)
|
||||
* [x] Non-latin character layouts (Persian)
|
||||
Norwegian, Swedish/Finnish, Icelandic, Danish, Hungarian,
|
||||
Croatian, Polish, Romanian); more coming in future versions
|
||||
* [x] Non-latin character layouts (Arabic, Persian, Greek, Russian
|
||||
(JCUKEN))
|
||||
* [x] Adapt to situation in app (password, url, text, etc. )
|
||||
* [x] Special character layout(s)
|
||||
* [x] Numeric layout
|
||||
* [x] Numeric layout (advanced)
|
||||
* [x] Phone number layout
|
||||
* [x] Emoji layout (popups buggy atm)
|
||||
* [x] Emoji layout
|
||||
* [x] Emoticon layout
|
||||
* [ ] Kaomoji layout
|
||||
|
||||
### Preferences
|
||||
* [x] Setup wizard
|
||||
* [x] Preferences screen
|
||||
* [x] Customize look and behaviour of keyboard (currently only
|
||||
light/dark theme)
|
||||
* [x] Customize look and behaviour of keyboard
|
||||
* [x] Theme presets (currently only day/night theme + borderless)
|
||||
* [x] Theme customization
|
||||
* [ ] Theme import/export (?)
|
||||
* [x] Subtype selection (language/layout)
|
||||
* [x] Keyboard behaviour preferences
|
||||
* [ ] Text suggestion / Auto correct preferences
|
||||
* [ ] Gesture preferences
|
||||
|
||||
### Composing suggestions
|
||||
* [ ] Auto suggest words from precompiled dictionary
|
||||
* [ ] Auto suggest words from user dictionary
|
||||
* [ ] Auto suggest contacts
|
||||
* [ ] Multilingual typing
|
||||
* [x] Gesture preferences
|
||||
|
||||
### Other useful features
|
||||
* [x] One-handed mode
|
||||
* [x] Clipboard/cursor tools
|
||||
* [ ] Floating keyboard
|
||||
* [ ] Gesture support
|
||||
* [ ] Glide typing (?)
|
||||
* [x] Integrated number row / symbols in character layouts
|
||||
* [x] Gesture support
|
||||
* [x] Full integration in IME service list of Android (xml/method)
|
||||
(integration is internal-only, because Android's default subtype
|
||||
implementation not really allows for dynamic language/layout
|
||||
pairs, only compile-time defined ones)
|
||||
* [ ] Description and settings reference in System Language & Input
|
||||
* [ ] (dev only) Generate well-structured documentation of code
|
||||
* [ ] ...
|
||||
|
||||
Note: (?) = not sure if it will be implemented
|
||||
## Feature roadmap
|
||||
This section describes the features which are planned to be implemented
|
||||
in FlorisBoard for the next major versions, modularized into sections.
|
||||
Please note that the milestone due dates are only raw estimates and will
|
||||
most likely be delayed back, even though I'm eager to stick to these as
|
||||
close as possible.
|
||||
|
||||
### [v0.4.0](https://github.com/florisboard/florisboard/milestone/4)
|
||||
- Module A: Smartbar rework (Implemented with #91)
|
||||
- Ability to enable/disable Smartbar (features below thus only work if
|
||||
Smartbar is enabled)
|
||||
- Dynamic switching between clipboard tools and word suggestions
|
||||
- Ability to show both the number row and word suggestions at once
|
||||
- Better icons in quick actions
|
||||
- Complete rework of the Smartbar code base and the Smartbar layout
|
||||
definition in XML
|
||||
|
||||
- Module B: Composing suggestions
|
||||
- Auto-suggestion of words based of precompiled dictionaries
|
||||
- Management of custom dictionary entries
|
||||
- Opt-in only: Learning of often typed word pais to better predict next
|
||||
words over time. Data collected here is stored locally and never leaves
|
||||
the user's device.
|
||||
|
||||
- Module C: Extension packs (base implementation with #162)
|
||||
- Ability to load dictionaries (and later potentially other cool
|
||||
features too) only if needed to keep the core APK size small
|
||||
- Currently unclear how exactly this will work, but this is definitely
|
||||
a must-have feature
|
||||
|
||||
- Module D: Glide typing
|
||||
- Swiping over the characters will automatically convert this to a word
|
||||
- Possibly also add improvements based on the Flow keyboard
|
||||
|
||||
- Module E: Theme rework (Implemented with #162)
|
||||
- Themes are now based on the Asset schema
|
||||
- Dynamic theme creation
|
||||
- Different theme modes (`Always day`, `Always dark`, `Follow system`
|
||||
and `Follow time`)
|
||||
- Define a separate theme both for day and night theme
|
||||
- Adapt to app theme if possible
|
||||
|
||||
### [v0.5.0](https://github.com/florisboard/florisboard/milestone/5)
|
||||
There's no exact roadmap yet but it is planned that the media part of
|
||||
FlorisBoard (emojis, emoticons, kaomoji) gets a rework. Also as an extension
|
||||
(requires v0.4.0/Module C) GIF support is planned.
|
||||
|
||||
### > v0.5.0
|
||||
This is completely open as of now and will gather planned features as time
|
||||
passes...
|
||||
|
||||
Backlog (currently not assigned to any milestone):
|
||||
|
||||
- Theme import/export
|
||||
- Floating keyboard
|
||||
|
||||
## Contributing
|
||||
Wanna contribute to FlorisBoard? That's great to hear! There are lots of
|
||||
different ways to help out. Bug reporting, making pull requests,
|
||||
translating FlorisBoard to make it more accessible, etc. For more
|
||||
information see the . Thank
|
||||
you for your help!
|
||||
|
||||
## List of permissions FlorisBoard requests
|
||||
Please refer to this [page](https://github.com/florisboard/florisboard/wiki/List-of-permissions-FlorisBoard-requests)
|
||||
to get more information on this topic.
|
||||
|
||||
## Used libraries, components and icons
|
||||
* [Google Flexbox Layout for Android](https://github.com/google/flexbox-layout)
|
||||
@@ -106,6 +174,12 @@ Note: (?) = not sure if it will be implemented
|
||||
[square](https://github.com/square)
|
||||
* [ColorPicker preference](https://github.com/jaredrummler/ColorPicker) by
|
||||
[Jared Rummler](https://github.com/jaredrummler)
|
||||
* [Timber](https://github.com/JakeWharton/timber) by
|
||||
[JakeWharton](https://github.com/JakeWharton)
|
||||
* [kotlin-result](https://github.com/michaelbull/kotlin-result) by
|
||||
[Michael Bull](https://github.com/michaelbull)
|
||||
* [expandable-fab](https://github.com/nambicompany/expandable-fab) by
|
||||
[Nambi](https://github.com/nambicompany)
|
||||
|
||||
## License
|
||||
```
|
||||
|
||||
@@ -6,12 +6,21 @@ android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "dev.patrickgold.florisboard"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 12
|
||||
versionName "0.2.0"
|
||||
versionCode 24
|
||||
versionName "0.3.5"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -21,9 +30,20 @@ android {
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
resValue "string", "app_name", "FlorisBoard Debug"
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
resValue "string", "app_name", "FlorisBoard"
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,19 +53,24 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'androidx.test:core:1.3.0'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
testImplementation 'org.robolectric:robolectric:4.4'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
implementation 'com.google.android:flexbox:2.0.1'
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.9.2"
|
||||
implementation 'com.google.android.material:material:1.2.0'
|
||||
implementation 'com.squareup.moshi:moshi-adapters:1.9.2'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result:1.1.9"
|
||||
implementation 'com.nambimobile.widgets:expandable-fab:1.0.2'
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<application
|
||||
android:name=".ime.core.FlorisApplication"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
@@ -66,6 +67,20 @@
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<!-- Theme Selector Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.settings.ThemeManagerActivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/settings__title"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Theme Editor Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.settings.ThemeEditorActivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/settings__theme_editor__title"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- About Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.settings.AboutActivity"
|
||||
@@ -90,6 +105,13 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/SettingsTheme"/>
|
||||
|
||||
<!-- Crash Dialog Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.crashutility.CrashDialogActivity"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/crash_dialog__title"
|
||||
android:theme="@style/CrashDialogTheme"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"qwerty": "QWERTY",
|
||||
"qwertz": "QWERTZ",
|
||||
"azerty": "AZERTY",
|
||||
"bepo": "BÉPO",
|
||||
"spanish": "Spanish (QWERTY)",
|
||||
"norwegian": "Norwegian (QWERTY)",
|
||||
"swedish_finnish": "Swedish/Finnish (QWERTY)",
|
||||
@@ -12,176 +13,177 @@
|
||||
"swiss_german": "Swiss German (QWERTZ)",
|
||||
"swiss_french": "Swiss French (QWERTZ)",
|
||||
"swiss_italian": "Swiss Italian (QWERTZ)",
|
||||
"persian": "Persian"
|
||||
"hungarian": "Hungarian (QWERTZ)",
|
||||
"persian": "Persian",
|
||||
"arabic": "Arabic",
|
||||
"esperanto": "Esperanto",
|
||||
"esperanto_with_hx": "Esperanto with 'ĥ'",
|
||||
"colemak": "Colemak",
|
||||
"dvorak": "Dvorak",
|
||||
"jcuken_russian": "Russian (JCUKEN)",
|
||||
"canadian_french": "Canadian French (QWERTY)",
|
||||
"greek": "Ελληνικά"
|
||||
},
|
||||
"defaultSubtypes": [
|
||||
{
|
||||
"id": 101,
|
||||
"languageTag": "en-US",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"languageTag": "en-UK",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"languageTag": "en-CA",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"languageTag": "en-AU",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 201,
|
||||
"languageTag": "de-DE",
|
||||
"preferredLayout": "qwertz",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwertz"
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"languageTag": "de-AT",
|
||||
"preferredLayout": "qwertz",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwertz"
|
||||
},
|
||||
{
|
||||
"id": 203,
|
||||
"languageTag": "de-CH",
|
||||
"preferredLayout": "swiss_german",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "swiss_german"
|
||||
},
|
||||
{
|
||||
"id": 301,
|
||||
"languageTag": "fr-FR",
|
||||
"preferredLayout": "azerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "azerty"
|
||||
},
|
||||
{
|
||||
"id": 302,
|
||||
"languageTag": "fr-CA",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "canadian_french"
|
||||
},
|
||||
{
|
||||
"id": 303,
|
||||
"languageTag": "fr-CH",
|
||||
"preferredLayout": "swiss_french",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "swiss_french"
|
||||
},
|
||||
{
|
||||
"id": 401,
|
||||
"languageTag": "it-IT",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 402,
|
||||
"languageTag": "it-CH",
|
||||
"preferredLayout": "swiss_italian",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "swiss_italian"
|
||||
},
|
||||
{
|
||||
"id": 501,
|
||||
"languageTag": "es-ES",
|
||||
"preferredLayout": "spanish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "spanish"
|
||||
},
|
||||
{
|
||||
"id": 502,
|
||||
"languageTag": "es-US",
|
||||
"preferredLayout": "spanish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "spanish"
|
||||
},
|
||||
{
|
||||
"id": 503,
|
||||
"languageTag": "es-419",
|
||||
"preferredLayout": "spanish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "spanish"
|
||||
},
|
||||
{
|
||||
"id": 601,
|
||||
"languageTag": "pt-PT",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 602,
|
||||
"languageTag": "pt-BR",
|
||||
"preferredLayout": "qwerty",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 701,
|
||||
"languageTag": "nb-NO",
|
||||
"preferredLayout": "norwegian",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "norwegian"
|
||||
},
|
||||
{
|
||||
"id": 702,
|
||||
"languageTag": "nn-NO",
|
||||
"preferredLayout": "norwegian",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "norwegian"
|
||||
},
|
||||
{
|
||||
"id": 711,
|
||||
"languageTag": "sv-SE",
|
||||
"preferredLayout": "swedish_finnish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "swedish_finnish"
|
||||
},
|
||||
{
|
||||
"id": 721,
|
||||
"languageTag": "fi-FI",
|
||||
"preferredLayout": "swedish_finnish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "swedish_finnish"
|
||||
},
|
||||
{
|
||||
"id": 731,
|
||||
"languageTag": "da-DK",
|
||||
"preferredLayout": "danish",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "danish"
|
||||
},
|
||||
{
|
||||
"id": 741,
|
||||
"languageTag": "is-IS",
|
||||
"preferredLayout": "icelandic",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "icelandic"
|
||||
},
|
||||
{
|
||||
"id": 800,
|
||||
"id": 801,
|
||||
"languageTag": "fa-FA",
|
||||
"preferredLayout": "persian",
|
||||
"isAsciiCapable": true,
|
||||
"isEmojiCapable": true
|
||||
"preferredLayout": "persian"
|
||||
},
|
||||
{
|
||||
"id": 901,
|
||||
"languageTag": "ar",
|
||||
"preferredLayout": "arabic"
|
||||
},
|
||||
{
|
||||
"id": 1001,
|
||||
"languageTag": "hu",
|
||||
"preferredLayout": "hungarian"
|
||||
},
|
||||
{
|
||||
"id": 1101,
|
||||
"languageTag": "eo",
|
||||
"preferredLayout": "esperanto"
|
||||
},
|
||||
{
|
||||
"id": 1201,
|
||||
"languageTag": "hr",
|
||||
"preferredLayout": "qwertz"
|
||||
},
|
||||
{
|
||||
"id": 1301,
|
||||
"languageTag": "ru",
|
||||
"preferredLayout": "jcuken_russian"
|
||||
},
|
||||
{
|
||||
"id": 1401,
|
||||
"languageTag": "el",
|
||||
"preferredLayout": "greek"
|
||||
},
|
||||
{
|
||||
"id": 1501,
|
||||
"languageTag": "ro",
|
||||
"preferredLayout": "qwerty"
|
||||
},
|
||||
{
|
||||
"id": 1601,
|
||||
"languageTag": "pl",
|
||||
"preferredLayout": "qwerty"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
47
app/src/main/assets/ime/text/characters/arabic.json
Normal file
47
app/src/main/assets/ime/text/characters/arabic.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "arabic",
|
||||
"authors": [ "HeiWiper" ],
|
||||
"direction": "rtl",
|
||||
"modifier": "arabic",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 1590, "label": "ض" },
|
||||
{ "code": 1589, "label": "ص" },
|
||||
{ "code": 1579, "label": "ث" },
|
||||
{ "code": 1602, "label": "ق" },
|
||||
{ "code": 1601, "label": "ف" },
|
||||
{ "code": 1594, "label": "غ" },
|
||||
{ "code": 1593, "label": "ع" },
|
||||
{ "code": 1607, "label": "ه" },
|
||||
{ "code": 1582, "label": "خ" },
|
||||
{ "code": 1581, "label": "ح" },
|
||||
{ "code": 1580, "label": "ج" }
|
||||
],
|
||||
[
|
||||
{ "code": 1588, "label": "ش" },
|
||||
{ "code": 1587, "label": "س" },
|
||||
{ "code": 1610, "label": "ي" },
|
||||
{ "code": 1576, "label": "ب" },
|
||||
{ "code": 1604, "label": "ل" },
|
||||
{ "code": 1575, "label": "ا" },
|
||||
{ "code": 1578, "label": "ت" },
|
||||
{ "code": 1606, "label": "ن" },
|
||||
{ "code": 1605, "label": "م" },
|
||||
{ "code": 1603, "label": "ك" },
|
||||
{ "code": 1591, "label": "ط" }
|
||||
],
|
||||
[
|
||||
{ "code": 1584, "label": "ذ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 65157, "label": "ﺅ" },
|
||||
{ "code": 1585, "label": "ر" },
|
||||
{ "code": 1609, "label": "ى" },
|
||||
{ "code": 1577, "label": "ة" },
|
||||
{ "code": 1608, "label": "و" },
|
||||
{ "code": 1586, "label": "ز" },
|
||||
{ "code": 1592, "label": "ظ" },
|
||||
{ "code": 1583, "label": "د" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "azerty",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,7 +15,8 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 113, "label": "q" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -25,20 +27,23 @@
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 109, "label": "m" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 119, "label": "w" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 39, "label": "'", "popup": [
|
||||
{ "code": 8218, "label": "‚" },
|
||||
{ "code": 8216, "label": "‘" },
|
||||
{ "code": 8217, "label": "’" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 8250, "label": "›" }
|
||||
] }
|
||||
{ "code": 39, "label": "'", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 8218, "label": "‚" },
|
||||
{ "code": 8216, "label": "‘" },
|
||||
{ "code": 8217, "label": "’" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 8250, "label": "›" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
51
app/src/main/assets/ime/text/characters/bepo.json
Normal file
51
app/src/main/assets/ime/text/characters/bepo.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "bepo",
|
||||
"authors": [ "salamandar" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 119, "label": "w" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 109, "label": "m" },
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 113, "label": "q", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 8218, "label": "‚" },
|
||||
{ "code": 8216, "label": "‘" },
|
||||
{ "code": 8217, "label": "’" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 8250, "label": "›" }
|
||||
]
|
||||
} },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 102, "label": "f" }
|
||||
]
|
||||
]
|
||||
}
|
||||
43
app/src/main/assets/ime/text/characters/canadian_french.json
Normal file
43
app/src/main/assets/ime/text/characters/canadian_french.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "canadian_french",
|
||||
"authors": [ "The-Quantum-Alpha" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 113, "label": "q" },
|
||||
{ "code": 119, "label": "w" },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 232, "label": "è" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 224, "label": "à" }
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 109, "label": "m" }
|
||||
]
|
||||
]
|
||||
}
|
||||
45
app/src/main/assets/ime/text/characters/colemak.json
Normal file
45
app/src/main/assets/ime/text/characters/colemak.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "colemak",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 113, "label": "q" },
|
||||
{ "code": 119, "label": "w" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 59, "label": ";", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 58, "label": ":" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" }
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 109, "label": "m" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "danish",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -15,7 +16,8 @@
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 229, "label": "å" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,7 +29,8 @@
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
64
app/src/main/assets/ime/text/characters/dvorak.json
Normal file
64
app/src/main/assets/ime/text/characters/dvorak.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "dvorak",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "dvorak",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 64, "label": "@", "groupId": 101, "variation": "email_address" },
|
||||
{ "code": 39, "label": "'", "groupId": 101, "variation": "normal", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 34, "label": "\"" }
|
||||
]
|
||||
} },
|
||||
{ "code": 39, "label": "'", "groupId": 101, "variation": "password", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 34, "label": "\"" }
|
||||
]
|
||||
} },
|
||||
{ "code": 47, "label": "/", "groupId": 101, "variation": "uri" },
|
||||
{ "code": 44, "label": ",", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 60, "label": "<" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
} },
|
||||
{ "code": 46, "label": ".", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 62, "label": ">" }
|
||||
]
|
||||
} },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 108, "label": "l" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 115, "label": "s" }
|
||||
],
|
||||
[
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 109, "label": "m" },
|
||||
{ "code": 119, "label": "w" },
|
||||
{ "code": 118, "label": "v" }
|
||||
]
|
||||
]
|
||||
}
|
||||
49
app/src/main/assets/ime/text/characters/esperanto.json
Normal file
49
app/src/main/assets/ime/text/characters/esperanto.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "esperanto",
|
||||
"authors": [ "jeremiah-miller", "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 349, "label": "ŝ", "popup": {
|
||||
"main": { "code": 113, "label": "q" }
|
||||
} },
|
||||
{ "code": 285, "label": "ĝ", "popup": {
|
||||
"main": { "code": 119, "label": "w" }
|
||||
} },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 365, "label": "ŭ", "popup": {
|
||||
"main": { "code": 121, "label": "y" }
|
||||
} },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 309, "label": "ĵ" }
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 265, "label": "ĉ", "popup": {
|
||||
"main": { "code": 120, "label": "x" }
|
||||
} },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 109, "label": "m" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "esperanto_with_hx",
|
||||
"authors": [ "jeremiah-miller", "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 349, "label": "ŝ", "popup": {
|
||||
"main": { "code": 113, "label": "q" }
|
||||
} },
|
||||
{ "code": 285, "label": "ĝ", "popup": {
|
||||
"main": { "code": 119, "label": "w" }
|
||||
} },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 365, "label": "ŭ", "popup": {
|
||||
"main": { "code": 121, "label": "y" }
|
||||
} },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 309, "label": "ĵ" }
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 265, "label": "ĉ", "popup": {
|
||||
"main": { "code": 120, "label": "x" }
|
||||
} },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 109, "label": "m" },
|
||||
{ "code": 293, "label": "ĥ" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "$default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"~enter": {
|
||||
"main": { "code": -215, "label": "toggle_one_handed_mode", "type": "system_gui" },
|
||||
"relevant": [
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" }
|
||||
]
|
||||
},
|
||||
"~left": {
|
||||
"main": { "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
"relevant": [
|
||||
{ "code": -215, "label": "toggle_one_handed_mode", "type": "system_gui" },
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
app/src/main/assets/ime/text/characters/extended_popups/ar.json
Normal file
148
app/src/main/assets/ime/text/characters/extended_popups/ar.json
Normal file
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "ar",
|
||||
"authors": [ "HeiWiper" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"ض": {
|
||||
"relevant": [
|
||||
{ "code": 1633, "label": "١" }
|
||||
]
|
||||
},
|
||||
"ص": {
|
||||
"relevant": [
|
||||
{ "code": 1634, "label": "٢" }
|
||||
]
|
||||
},
|
||||
"ث": {
|
||||
"relevant": [
|
||||
{ "code": 1635, "label": "٣" }
|
||||
]
|
||||
},
|
||||
"ق": {
|
||||
"relevant": [
|
||||
{ "code": 1704, "label": "ڨ" },
|
||||
{ "code": 1636, "label": "٤" }
|
||||
]
|
||||
},
|
||||
"ف": {
|
||||
"relevant": [
|
||||
{ "code": 1701, "label": "ڥ" },
|
||||
{ "code": 1700, "label": "ڤ" },
|
||||
{ "code": 1698, "label": "ڢ" },
|
||||
{ "code": 1637, "label": "٥" }
|
||||
]
|
||||
},
|
||||
"غ": {
|
||||
"relevant": [
|
||||
{ "code": 1638, "label": "٦" }
|
||||
]
|
||||
},
|
||||
"ع": {
|
||||
"relevant": [
|
||||
{ "code": 1639, "label": "٧" }
|
||||
]
|
||||
},
|
||||
"ه": {
|
||||
"relevant": [
|
||||
{ "code": 1726, "label": "ھ" },
|
||||
{ "code": 1640, "label": "٨" }
|
||||
]
|
||||
},
|
||||
"خ": {
|
||||
"relevant": [
|
||||
{ "code": 1641, "label": "٩" }
|
||||
]
|
||||
},
|
||||
"ح": {
|
||||
"relevant": [
|
||||
{ "code": 1632, "label": "٠" }
|
||||
]
|
||||
},
|
||||
"ج": {
|
||||
"relevant": [
|
||||
{ "code": 1670, "label": "چ" }
|
||||
]
|
||||
},
|
||||
"ش": {
|
||||
"relevant": [
|
||||
{ "code": 1692, "label": "ڜ" }
|
||||
]
|
||||
},
|
||||
"ي": {
|
||||
"relevant": [
|
||||
{ "code": 1574, "label": "ئ" },
|
||||
{ "code": 1609, "label": "ى" }
|
||||
]
|
||||
},
|
||||
"ب": {
|
||||
"relevant": [
|
||||
{ "code": 1662, "label": "پ" }
|
||||
]
|
||||
},
|
||||
"ل": {
|
||||
"relevant": [
|
||||
{ "code": 65275, "label": "لا" },
|
||||
{ "code": 65273, "label": "لإ" },
|
||||
{ "code": 65271, "label": "لأ" },
|
||||
{ "code": 65269, "label": "لآ" }
|
||||
]
|
||||
},
|
||||
"ا": {
|
||||
"relevant": [
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 1571, "label": "أ" },
|
||||
{ "code": 1573, "label": "إ" },
|
||||
{ "code": 1649, "label": "ٱ" }
|
||||
]
|
||||
},
|
||||
"ك": {
|
||||
"relevant": [
|
||||
{ "code": 1705, "label": "ک"},
|
||||
{ "code": 1711, "label": "گ" }
|
||||
]
|
||||
},
|
||||
"ى": {
|
||||
"relevant": [
|
||||
{ "code": 1574, "label": "ئ" }
|
||||
]
|
||||
},
|
||||
"ز": {
|
||||
"relevant": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 1611, "label": "ً" },
|
||||
"relevant": [
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
{ "code": 1615, "label": "ُ" },
|
||||
{ "code": 1616, "label": "ِ" },
|
||||
{ "code": 1614, "label": "َ" },
|
||||
{ "code": 1600, "label": "ـ" },
|
||||
{ "code": 1621, "label": "ٕ" },
|
||||
{ "code": 1620, "label": "ٔ" },
|
||||
{ "code": 1617, "label": "ّ" },
|
||||
{ "code": 1612, "label": "ٌ" },
|
||||
{ "code": 1613, "label": "ٍ" },
|
||||
{ "code": 1618, "label": "ْ" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".ir"},
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +1,133 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"d": [
|
||||
{ "code": 240, "label": "ð" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"l": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" },
|
||||
{ "code": 353, "label": "š" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
"y": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
"æ": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"ø": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "da",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"relevant": [
|
||||
{ "code": 240, "label": "ð" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
]
|
||||
},
|
||||
"l": {
|
||||
"relevant": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" },
|
||||
{ "code": 353, "label": "š" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
]
|
||||
},
|
||||
"æ": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"ø": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".eu" },
|
||||
{ "code": -255, "label": ".dk" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,109 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "de",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"main": { "code": 228, "label": "ä" },
|
||||
"relevant": [
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"main": { "code": 246, "label": "ö" },
|
||||
"relevant": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"main": { "code": 223, "label": "ß" },
|
||||
"relevant": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"main": { "code": 252, "label": "ü" },
|
||||
"relevant": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".ch" },
|
||||
{ "code": -255, "label": ".de" },
|
||||
{ "code": -255, "label": ".at" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "el",
|
||||
"authors": [ "tsiflimagas" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"α": {
|
||||
"relevant": [
|
||||
{ "code": 940, "label": "ά" }
|
||||
]
|
||||
},
|
||||
"ε": {
|
||||
"relevant": [
|
||||
{ "code": 941, "label": "έ" }
|
||||
]
|
||||
},
|
||||
"η": {
|
||||
"relevant": [
|
||||
{ "code": 942, "label": "ή" }
|
||||
]
|
||||
},
|
||||
"ι": {
|
||||
"relevant": [
|
||||
{ "code": 912, "label": "ΐ" },
|
||||
{ "code": 970, "label": "ϊ" },
|
||||
{ "code": 943, "label": "ί" }
|
||||
]
|
||||
},
|
||||
"ο": {
|
||||
"relevant": [
|
||||
{ "code": 972, "label": "ό" }
|
||||
]
|
||||
},
|
||||
"υ": {
|
||||
"relevant": [
|
||||
{ "code": 944, "label": "ΰ" },
|
||||
{ "code": 971, "label": "ϋ" },
|
||||
{ "code": 973, "label": "ύ" }
|
||||
]
|
||||
},
|
||||
"ω": {
|
||||
"relevant": [
|
||||
{ "code": 974, "label": "ώ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".gr" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,107 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "en",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 244, "label": "ô" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "eo",
|
||||
"authors": [ "jeremiah-miller" ],
|
||||
"mapping":{
|
||||
"all": {
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 265, "label": "ĉ" }
|
||||
]
|
||||
},
|
||||
"g": {
|
||||
"relevant": [
|
||||
{ "code": 285, "label": "ĝ" }
|
||||
]
|
||||
},
|
||||
"h": {
|
||||
"relevant": [
|
||||
{ "code": 293, "label": "ĥ" }
|
||||
]
|
||||
},
|
||||
"j": {
|
||||
"relevant": [
|
||||
{ "code": 309, "label": "ĵ" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 349, "label": "ŝ" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 365, "label": "ŭ" }
|
||||
]
|
||||
},
|
||||
"q": {
|
||||
"relevant": [
|
||||
{ "code": 349, "label": "ŝ" }
|
||||
]
|
||||
},
|
||||
"w": {
|
||||
"relevant": [
|
||||
{ "code": 285, "label": "ĝ" }
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"relevant": [
|
||||
{ "code": 265, "label": "ĉ" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 365, "label": "ŭ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +1,115 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 161, "label": "¡" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 191, "label": "¿" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "es",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"main": { "code": 225, "label": "á" },
|
||||
"relevant": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"main": { "code": 243, "label": "ó" },
|
||||
"relevant": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 242, "label": "ò" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 223, "label": "ß" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".com.es" },
|
||||
{ "code": -255, "label": ".es" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,125 @@
|
||||
{
|
||||
"ض": [
|
||||
{ "code": 1777, "label": "۱" }
|
||||
],
|
||||
"ص": [
|
||||
{ "code": 1778, "label": "۲" }
|
||||
],
|
||||
"ث": [
|
||||
{ "code": 1779, "label": "۳" }
|
||||
],
|
||||
"ق": [
|
||||
{ "code": 1780, "label": "۴" }
|
||||
],
|
||||
"ف": [
|
||||
{ "code": 1781, "label": "۵" }
|
||||
],
|
||||
"غ": [
|
||||
{ "code": 1782, "label": "۶" }
|
||||
],
|
||||
"ع": [
|
||||
{ "code": 1783, "label": "۷" }
|
||||
],
|
||||
"ه": [
|
||||
{ "code": 1784, "label": "۸" }
|
||||
],
|
||||
"خ": [
|
||||
{ "code": 1785, "label": "۹" }
|
||||
],
|
||||
"ح": [
|
||||
{ "code": 1776, "label": "۰" }
|
||||
],
|
||||
"ی": [
|
||||
{ "code": 1574, "label": "ئ" },
|
||||
{ "code": 1610, "label": "ي" }
|
||||
],
|
||||
"ا": [
|
||||
{ "code": 1649, "label": "ٱ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1571, "label": "أ" },
|
||||
{ "code": 1573, "label": "إ" }
|
||||
],
|
||||
"ت": [
|
||||
{ "code": 1577, "label": "ة" }
|
||||
],
|
||||
"ک": [
|
||||
{ "code": 1603, "label": "ك" },
|
||||
{ "code": 1706, "label": "ڪ"}
|
||||
],
|
||||
"ز": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
],
|
||||
"و": [
|
||||
{ "code": 1572, "label": "ؤ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
{ "code": 1615, "label": "ُ" },
|
||||
{ "code": 1616, "label": "ِ" },
|
||||
{ "code": 1614, "label": "َ" },
|
||||
{ "code": 1600, "label": "ـ" },
|
||||
{ "code": 1621, "label": "ٕ" },
|
||||
{ "code": 1618, "label": "ْ" },
|
||||
{ "code": 1617, "label": "ّ" },
|
||||
{ "code": 1612, "label": "ٌ" },
|
||||
{ "code": 1613, "label": "ٍ" },
|
||||
{ "code": 1611, "label": "ً" },
|
||||
{ "code": 1620, "label": "ٔ" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" },
|
||||
{ "code": -255, "label": ".ir"}
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "fa",
|
||||
"authors": [ "PHELAT" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"ض": {
|
||||
"relevant": [
|
||||
{ "code": 1777, "label": "۱" }
|
||||
]
|
||||
},
|
||||
"ص": {
|
||||
"relevant": [
|
||||
{ "code": 1778, "label": "۲" }
|
||||
]
|
||||
},
|
||||
"ث": {
|
||||
"relevant": [
|
||||
{ "code": 1779, "label": "۳" }
|
||||
]
|
||||
},
|
||||
"ق": {
|
||||
"relevant": [
|
||||
{ "code": 1780, "label": "۴" }
|
||||
]
|
||||
},
|
||||
"ف": {
|
||||
"relevant": [
|
||||
{ "code": 1781, "label": "۵" }
|
||||
]
|
||||
},
|
||||
"غ": {
|
||||
"relevant": [
|
||||
{ "code": 1782, "label": "۶" }
|
||||
]
|
||||
},
|
||||
"ع": {
|
||||
"relevant": [
|
||||
{ "code": 1783, "label": "۷" }
|
||||
]
|
||||
},
|
||||
"ه": {
|
||||
"relevant": [
|
||||
{ "code": 1784, "label": "۸" }
|
||||
]
|
||||
},
|
||||
"خ": {
|
||||
"relevant": [
|
||||
{ "code": 1785, "label": "۹" }
|
||||
]
|
||||
},
|
||||
"ح": {
|
||||
"relevant": [
|
||||
{ "code": 1776, "label": "۰" }
|
||||
]
|
||||
},
|
||||
"ی": {
|
||||
"relevant": [
|
||||
{ "code": 1574, "label": "ئ" },
|
||||
{ "code": 1610, "label": "ي" }
|
||||
]
|
||||
},
|
||||
"ا": {
|
||||
"relevant": [
|
||||
{ "code": 1570, "label": "آ" },
|
||||
{ "code": 1649, "label": "ٱ" },
|
||||
{ "code": 1569, "label": "ء" },
|
||||
{ "code": 1571, "label": "أ" },
|
||||
{ "code": 1573, "label": "إ" }
|
||||
]
|
||||
},
|
||||
"ت": {
|
||||
"relevant": [
|
||||
{ "code": 1577, "label": "ة" }
|
||||
]
|
||||
},
|
||||
"ک": {
|
||||
"relevant": [
|
||||
{ "code": 1706, "label": "ڪ"},
|
||||
{ "code": 1603, "label": "ك" }
|
||||
]
|
||||
},
|
||||
"ز": {
|
||||
"relevant": [
|
||||
{ "code": 1688, "label": "ژ" }
|
||||
]
|
||||
},
|
||||
"و": {
|
||||
"relevant": [
|
||||
{ "code": 1572, "label": "ؤ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 1611, "label": "ً" },
|
||||
"relevant": [
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
{ "code": 1615, "label": "ُ" },
|
||||
{ "code": 1616, "label": "ِ" },
|
||||
{ "code": 1614, "label": "َ" },
|
||||
{ "code": 1600, "label": "ـ" },
|
||||
{ "code": 1621, "label": "ٕ" },
|
||||
{ "code": 1618, "label": "ْ" },
|
||||
{ "code": 1617, "label": "ّ" },
|
||||
{ "code": 1612, "label": "ٌ" },
|
||||
{ "code": 1613, "label": "ٍ" },
|
||||
{ "code": 1620, "label": "ٔ" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".ir"},
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,118 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 224, "label": "à" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
"z": [
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 382, "label": "ž" },
|
||||
{ "code": 378, "label": "ź" }
|
||||
],
|
||||
"ä": [
|
||||
{ "code": 230, "label": "æ" }
|
||||
],
|
||||
"ö": [
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "fi",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
]
|
||||
},
|
||||
"z": {
|
||||
"relevant": [
|
||||
{ "code": 382, "label": "ž" },
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 378, "label": "ź" }
|
||||
]
|
||||
},
|
||||
"ä": {
|
||||
"relevant": [
|
||||
{ "code": 230, "label": "æ" }
|
||||
]
|
||||
},
|
||||
"ö": {
|
||||
"relevant": [
|
||||
{ "code": 248, "label": "ø" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +1,121 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 339, "label": "œ" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
"y": [
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "fr",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 339, "label": "œ" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 223, "label": "ß" },
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 347, "label": "ś" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "hr",
|
||||
"authors": [ "hedidnothingwrong" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"relevant": [
|
||||
{ "code": 273, "label": "đ" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 353, "label": "š" }
|
||||
]
|
||||
},
|
||||
"z": {
|
||||
"relevant": [
|
||||
{ "code": 382, "label": "ž" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".eu" },
|
||||
{ "code": -255, "label": ".hr" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "hu",
|
||||
"authors": [ "zoli111" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 225, "label": "á" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 337, "label": "ő" }
|
||||
]
|
||||
},
|
||||
"ö": {
|
||||
"relevant": [
|
||||
{ "code": 337, "label": "ő" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 369, "label": "ű" }
|
||||
]
|
||||
},
|
||||
"ü": {
|
||||
"relevant": [
|
||||
{ "code": 369, "label": "ű" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".hu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,110 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 229, "label": "å" }
|
||||
],
|
||||
"d": [
|
||||
{ "code": 240, "label": "ð" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"t": [
|
||||
{ "code": 254, "label": "þ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
],
|
||||
"y": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "is",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 229, "label": "å" }
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"relevant": [
|
||||
{ "code": 240, "label": "ð" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 239, "label": "ï" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"t": {
|
||||
"relevant": [
|
||||
{ "code": 254, "label": "þ" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 249, "label": "ù" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,103 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 243, "label": "ó" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "it",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 230, "label": "æ" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".it" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,99 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
"æ": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"ø": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "nb",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
]
|
||||
},
|
||||
"æ": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"ø": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +1,109 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 236, "label": "ì" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
],
|
||||
"y": [
|
||||
{ "code": 7923, "label": "ỳ" }
|
||||
],
|
||||
"æ": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
],
|
||||
"ø": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "nn",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 257, "label": "ā" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 236, "label": "ì" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 7923, "label": "ỳ" }
|
||||
]
|
||||
},
|
||||
"æ": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"ø": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "pl",
|
||||
"authors": [ "Mikołaj Biel" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 281, "label": "ę" }
|
||||
]
|
||||
},
|
||||
"l": {
|
||||
"relevant": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 243, "label": "ó" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 347, "label": "ś" }
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"relevant": [
|
||||
{ "code": 378, "label": "ź" }
|
||||
]
|
||||
},
|
||||
"z": {
|
||||
"relevant": [
|
||||
{ "code": 378, "label": "ź" },
|
||||
{ "code": 380, "label": "ż" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".pl" },
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "pt-BR",
|
||||
"authors": [ "rickym7" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 281, "label": "ę" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 243, "label": "ó" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 251, "label": "û" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".br" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,109 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 263, "label": "ć" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "pt",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 170, "label": "ª" },
|
||||
{ "code": 225, "label": "á" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" },
|
||||
{ "code": 269, "label": "č" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 279, "label": "ė" },
|
||||
{ "code": 275, "label": "ē" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 236, "label": "ì" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 238, "label": "î" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 241, "label": "ñ" },
|
||||
{ "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 186, "label": "º" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 245, "label": "õ" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 251, "label": "û" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".pt" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "ro",
|
||||
"authors": [ "bertin0" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"main": { "code": 259, "label": "ă" },
|
||||
"relevant": [
|
||||
{ "code": 226, "label": "â" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"main": { "code": 238, "label": "î" }
|
||||
},
|
||||
"s": {
|
||||
"main": {"code": 537, "label": "ș"}
|
||||
},
|
||||
"t": {
|
||||
"main": {"code": 539, "label": "ț"}
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".ro" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" },
|
||||
{ "code": -255, "label": ".edu" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"type": "characters/extended_popups",
|
||||
"name": "ru",
|
||||
"authors": [ "williamtheaker" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"е": {
|
||||
"relevant": [
|
||||
{ "code": 1105, "label": "ё" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".ru" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,160 @@
|
||||
{
|
||||
"a": [
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" }
|
||||
],
|
||||
"c": [
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
],
|
||||
"d": [
|
||||
{ "code": 240, "label": "ð" },
|
||||
{ "code": 271, "label": "ď" }
|
||||
],
|
||||
"e": [
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
],
|
||||
"i": [
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 236, "label": "ì" }
|
||||
],
|
||||
"l": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
],
|
||||
"n": [
|
||||
{ "code": 328, "label": "ň" },
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
],
|
||||
"o": [
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
"r": [
|
||||
{ "code": 345, "label": "ř" }
|
||||
],
|
||||
"s": [
|
||||
{ "code": 347, "label": "ś" },
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 351, "label": "ş" },
|
||||
{ "code": 223, "label": "ß" }
|
||||
],
|
||||
"t": [
|
||||
{ "code": 357, "label": "ť" },
|
||||
{ "code": 254, "label": "þ" }
|
||||
],
|
||||
"u": [
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 251, "label": "û" }
|
||||
],
|
||||
"y": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
],
|
||||
"z": [
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 378, "label": "ź" },
|
||||
{ "code": 382, "label": "ž" }
|
||||
],
|
||||
"ä": [
|
||||
{ "code": 230, "label": "æ" }
|
||||
],
|
||||
"ö": [
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
],
|
||||
".~normal": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 63, "label": "?" }
|
||||
],
|
||||
".~uri": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".com" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
"type": "characters/extended_popups",
|
||||
"name": "sv",
|
||||
"authors": [ "patrickgold" ],
|
||||
"mapping": {
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" },
|
||||
{ "code": 224, "label": "à" },
|
||||
{ "code": 226, "label": "â" },
|
||||
{ "code": 261, "label": "ą" },
|
||||
{ "code": 227, "label": "ã" },
|
||||
{ "code": 229, "label": "å" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 225, "label": "á" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "code": 231, "label": "ç" },
|
||||
{ "code": 269, "label": "č" },
|
||||
{ "code": 263, "label": "ć" }
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"relevant": [
|
||||
{ "code": 240, "label": "ð" },
|
||||
{ "code": 271, "label": "ď" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 234, "label": "ê" },
|
||||
{ "code": 232, "label": "è" },
|
||||
{ "code": 235, "label": "ë" },
|
||||
{ "code": 281, "label": "ę" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "code": 237, "label": "í" },
|
||||
{ "code": 239, "label": "ï" },
|
||||
{ "code": 299, "label": "ī" },
|
||||
{ "code": 303, "label": "į" },
|
||||
{ "code": 238, "label": "î" },
|
||||
{ "code": 236, "label": "ì" }
|
||||
]
|
||||
},
|
||||
"l": {
|
||||
"relevant": [
|
||||
{ "code": 322, "label": "ł" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "code": 324, "label": "ń" },
|
||||
{ "code": 328, "label": "ň" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 333, "label": "ō" },
|
||||
{ "code": 245, "label": "õ" },
|
||||
{ "code": 242, "label": "ò" },
|
||||
{ "code": 244, "label": "ô" },
|
||||
{ "code": 243, "label": "ó" },
|
||||
{ "code": 339, "label": "œ" },
|
||||
{ "code": 248, "label": "ø" }
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"relevant": [
|
||||
{ "code": 345, "label": "ř" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "code": 347, "label": "ś" },
|
||||
{ "code": 353, "label": "š" },
|
||||
{ "code": 351, "label": "ş" },
|
||||
{ "code": 223, "label": "ß" }
|
||||
]
|
||||
},
|
||||
"t": {
|
||||
"relevant": [
|
||||
{ "code": 357, "label": "ť" },
|
||||
{ "code": 254, "label": "þ" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "code": 252, "label": "ü" },
|
||||
{ "code": 363, "label": "ū" },
|
||||
{ "code": 249, "label": "ù" },
|
||||
{ "code": 250, "label": "ú" },
|
||||
{ "code": 251, "label": "û" }
|
||||
]
|
||||
},
|
||||
"y": {
|
||||
"relevant": [
|
||||
{ "code": 253, "label": "ý" },
|
||||
{ "code": 255, "label": "ÿ" }
|
||||
]
|
||||
},
|
||||
"z": {
|
||||
"relevant": [
|
||||
{ "code": 378, "label": "ź" },
|
||||
{ "code": 380, "label": "ż" },
|
||||
{ "code": 382, "label": "ž" }
|
||||
]
|
||||
},
|
||||
"ä": {
|
||||
"relevant": [
|
||||
{ "code": 230, "label": "æ" }
|
||||
]
|
||||
},
|
||||
"ö": {
|
||||
"relevant": [
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 339, "label": "œ" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
app/src/main/assets/ime/text/characters/greek.json
Normal file
40
app/src/main/assets/ime/text/characters/greek.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "greek",
|
||||
"authors": [ "tsiflimagas" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 894, "label": ";" },
|
||||
{ "code": 962, "label": "ς" },
|
||||
{ "code": 949, "label": "ε" },
|
||||
{ "code": 961, "label": "ρ" },
|
||||
{ "code": 964, "label": "τ" },
|
||||
{ "code": 965, "label": "υ" },
|
||||
{ "code": 952, "label": "θ" },
|
||||
{ "code": 953, "label": "ι" },
|
||||
{ "code": 959, "label": "ο" },
|
||||
{ "code": 960, "label": "π" }
|
||||
],
|
||||
[
|
||||
{ "code": 945, "label": "α" },
|
||||
{ "code": 963, "label": "σ" },
|
||||
{ "code": 948, "label": "δ" },
|
||||
{ "code": 966, "label": "φ" },
|
||||
{ "code": 947, "label": "γ" },
|
||||
{ "code": 951, "label": "η" },
|
||||
{ "code": 958, "label": "ξ" },
|
||||
{ "code": 954, "label": "κ" },
|
||||
{ "code": 955, "label": "λ" }
|
||||
],
|
||||
[
|
||||
{ "code": 950, "label": "ζ" },
|
||||
{ "code": 967, "label": "χ" },
|
||||
{ "code": 968, "label": "ψ" },
|
||||
{ "code": 969, "label": "ω" },
|
||||
{ "code": 946, "label": "β" },
|
||||
{ "code": 957, "label": "ν" },
|
||||
{ "code": 956, "label": "μ" }
|
||||
]
|
||||
]
|
||||
}
|
||||
44
app/src/main/assets/ime/text/characters/hungarian.json
Normal file
44
app/src/main/assets/ime/text/characters/hungarian.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "hungarian",
|
||||
"authors": [ "zoli111" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 113, "label": "q" },
|
||||
{ "code": 119, "label": "w" },
|
||||
{ "code": 101, "label": "e" },
|
||||
{ "code": 114, "label": "r" },
|
||||
{ "code": 116, "label": "t" },
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 117, "label": "u" },
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
{ "code": 102, "label": "f" },
|
||||
{ "code": 103, "label": "g" },
|
||||
{ "code": 104, "label": "h" },
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 233, "label": "é" },
|
||||
{ "code": 225, "label": "á" }
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
{ "code": 118, "label": "v" },
|
||||
{ "code": 98, "label": "b" },
|
||||
{ "code": 110, "label": "n" },
|
||||
{ "code": 109, "label": "m" },
|
||||
{ "code": 252, "label": "ü" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "icelandic",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -15,7 +16,8 @@
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 240, "label": "ð" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,7 +29,8 @@
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 230, "label": "æ" },
|
||||
{ "code": 246, "label": "ö" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
46
app/src/main/assets/ime/text/characters/jcuken_russian.json
Normal file
46
app/src/main/assets/ime/text/characters/jcuken_russian.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "jcuken_russian",
|
||||
"authors": [ "williamtheaker" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 1081, "label": "й" },
|
||||
{ "code": 1094, "label": "ц" },
|
||||
{ "code": 1091, "label": "у" },
|
||||
{ "code": 1082, "label": "к" },
|
||||
{ "code": 1077, "label": "е" },
|
||||
{ "code": 1085, "label": "н" },
|
||||
{ "code": 1075, "label": "г" },
|
||||
{ "code": 1096, "label": "ш" },
|
||||
{ "code": 1097, "label": "щ" },
|
||||
{ "code": 1079, "label": "з" },
|
||||
{ "code": 1093, "label": "х" },
|
||||
{ "code": 1098, "label": "ъ" }
|
||||
],
|
||||
[
|
||||
{ "code": 1092 , "label": "ф" },
|
||||
{ "code": 1099 , "label": "ы" },
|
||||
{ "code": 1074 , "label": "в" },
|
||||
{ "code": 1072 , "label": "а" },
|
||||
{ "code": 1087 , "label": "п" },
|
||||
{ "code": 1088 , "label": "р" },
|
||||
{ "code": 1086 , "label": "о" },
|
||||
{ "code": 1083 , "label": "л" },
|
||||
{ "code": 1076 , "label": "д" },
|
||||
{ "code": 1078 , "label": "ж" },
|
||||
{ "code": 1101 , "label": "э" }
|
||||
],
|
||||
[
|
||||
{ "code": 1103 , "label": "я" },
|
||||
{ "code": 1095 , "label": "ч" },
|
||||
{ "code": 1089 , "label": "с" },
|
||||
{ "code": 1084 , "label": "м" },
|
||||
{ "code": 1080 , "label": "и" },
|
||||
{ "code": 1090 , "label": "т" },
|
||||
{ "code": 1100 , "label": "ь" },
|
||||
{ "code": 1073 , "label": "б" },
|
||||
{ "code": 1102 , "label": "ю" }
|
||||
]
|
||||
]
|
||||
}
|
||||
27
app/src/main/assets/ime/text/characters/mod/arabic.json
Normal file
27
app/src/main/assets/ime/text/characters/mod/arabic.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"type": "characters/mod",
|
||||
"name": "arabic",
|
||||
"authors": [ "HeiWiper" ],
|
||||
"direction": "rtl",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
|
||||
{ "code": 1548, "label": "،", "groupId": 1, "variation": "normal" },
|
||||
{ "code": 1548, "label": "،", "groupId": 1, "variation": "password" },
|
||||
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,32 +1,28 @@
|
||||
{
|
||||
"type": "characters/mod",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": -1, "label": "shift", "type": "modifier" },
|
||||
{ "code": 0 },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 64, "label": "@", "variation": "email_address" },
|
||||
{ "code": 44, "label": ",", "variation": "normal" },
|
||||
{ "code": 47, "label": "/", "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui", "popup": [
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui", "popup": [
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": 32, "label": " " },
|
||||
{ "code": 46, "label": ".", "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing", "popup": [
|
||||
{ "code": -215, "label": "toggle_one_handed_mode", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" }
|
||||
] }
|
||||
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
|
||||
{ "code": 44, "label": ",", "groupId": 1, "variation": "normal" },
|
||||
{ "code": 44, "label": ",", "groupId": 1, "variation": "password" },
|
||||
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
25
app/src/main/assets/ime/text/characters/mod/dvorak.json
Normal file
25
app/src/main/assets/ime/text/characters/mod/dvorak.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "characters/mod",
|
||||
"name": "dvorak",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": -1, "label": "shift", "type": "modifier" },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 113, "label": "q", "groupId": 1 },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 122, "label": "z", "groupId": 2, "variation": "email_address" },
|
||||
{ "code": 122, "label": "z", "groupId": 2, "variation": "normal" },
|
||||
{ "code": 122, "label": "z", "groupId": 2, "variation": "password" },
|
||||
{ "code": 122, "label": "z", "groupId": 2, "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,34 +1,31 @@
|
||||
{
|
||||
"type": "characters/mod",
|
||||
"name": "persian",
|
||||
"authors": [ "PHELAT" ],
|
||||
"direction": "rtl",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 0 },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 64, "label": "@", "variation": "email_address" },
|
||||
{ "code": 1548, "label": "،", "variation": "normal" },
|
||||
{ "code": 47, "label": "/", "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui", "popup": [
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui", "popup": [
|
||||
{ "code": -100, "label": "settings", "type": "system_gui" }
|
||||
] },
|
||||
{ "code": 32, "label": " " },
|
||||
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
|
||||
{ "code": 1548, "label": "،", "groupId": 1, "variation": "normal" },
|
||||
{ "code": 1548, "label": "،", "groupId": 1, "variation": "password" },
|
||||
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
|
||||
{ "code": -210, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 8204, "label": "half_space", "variation": "normal" },
|
||||
{ "code": 8204, "label": "half_space", "variation": "password" },
|
||||
{ "code": 1600, "label": "kashida", "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing", "popup": [
|
||||
{ "code": -215, "label": "toggle_one_handed_mode", "type": "system_gui" },
|
||||
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" }
|
||||
] }
|
||||
{ "code": 1600, "label": "kashida", "variation": "password" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
|
||||
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "norwegian",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -15,7 +16,8 @@
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 229, "label": "å" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,7 +29,8 @@
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 248, "label": "ø" },
|
||||
{ "code": 230, "label": "æ" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "persian",
|
||||
"authors": [ "PHELAT" ],
|
||||
"direction": "rtl",
|
||||
"modifier": "persian",
|
||||
"arrangement": [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "qwerty",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,7 +15,8 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -24,7 +26,8 @@
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "qwertz",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,7 +15,8 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -24,7 +26,8 @@
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "spanish",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,7 +15,8 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -25,7 +27,8 @@
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 241, "label": "ñ" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "swedish_finnish",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -15,7 +16,8 @@
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 229, "label": "å" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,7 +29,8 @@
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 246, "label": "ö" },
|
||||
{ "code": 228, "label": "ä" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": 122, "label": "z" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "swiss_french",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,10 +15,13 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 232, "label": "è", "popup": [
|
||||
{ "code": 252, "label": "ü" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 232, "label": "è", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 252, "label": "ü" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,13 +31,18 @@
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 233, "label": "é", "popup": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
] },
|
||||
{ "code": 224, "label": "à", "popup": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 233, "label": "é", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 246, "label": "ö" }
|
||||
]
|
||||
} },
|
||||
{ "code": 224, "label": "à", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 228, "label": "ä" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "swiss_german",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,10 +15,13 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 252, "label": "ü", "popup": [
|
||||
{ "code": 232, "label": "è" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 252, "label": "ü", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 232, "label": "è" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,13 +31,18 @@
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 246, "label": "ö", "popup": [
|
||||
{ "code": 233, "label": "é" }
|
||||
] },
|
||||
{ "code": 228, "label": "ä", "popup": [
|
||||
{ "code": 224, "label": "à" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 246, "label": "ö", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" }
|
||||
]
|
||||
} },
|
||||
{ "code": 228, "label": "ä", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"type": "characters",
|
||||
"name": "swiss_italian",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
@@ -14,10 +15,13 @@
|
||||
{ "code": 105, "label": "i" },
|
||||
{ "code": 111, "label": "o" },
|
||||
{ "code": 112, "label": "p" },
|
||||
{ "code": 252, "label": "ü", "popup": [
|
||||
{ "code": 232, "label": "è" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 252, "label": "ü", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 232, "label": "è" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 97, "label": "a" },
|
||||
{ "code": 115, "label": "s" },
|
||||
{ "code": 100, "label": "d" },
|
||||
@@ -27,13 +31,18 @@
|
||||
{ "code": 106, "label": "j" },
|
||||
{ "code": 107, "label": "k" },
|
||||
{ "code": 108, "label": "l" },
|
||||
{ "code": 246, "label": "ö", "popup": [
|
||||
{ "code": 233, "label": "é" }
|
||||
] },
|
||||
{ "code": 228, "label": "ä", "popup": [
|
||||
{ "code": 224, "label": "à" }
|
||||
] }
|
||||
], [
|
||||
{ "code": 246, "label": "ö", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 233, "label": "é" }
|
||||
]
|
||||
} },
|
||||
{ "code": 228, "label": "ä", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 224, "label": "à" }
|
||||
]
|
||||
} }
|
||||
],
|
||||
[
|
||||
{ "code": 121, "label": "y" },
|
||||
{ "code": 120, "label": "x" },
|
||||
{ "code": 99, "label": "c" },
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "extension",
|
||||
"name": "clipboard_cursor_row",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": -135, "label": "clipboard_select_all", "type": "enter_editing" },
|
||||
{ "code": -130, "label": "clipboard_copy", "type": "enter_editing" },
|
||||
{ "code": -20, "label": "arrow_left", "type": "navigation" },
|
||||
{ "code": -21, "label": "arrow_right", "type": "navigation" },
|
||||
{ "code": -131, "label": "clipboard_cut", "type": "enter_editing" },
|
||||
{ "code": -132, "label": "clipboard_paste", "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,41 +1,74 @@
|
||||
{
|
||||
"type": "extension",
|
||||
"name": "number_row",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 49, "label": "1", "popup": [
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8539, "label": "⅛" }
|
||||
] },
|
||||
{ "code": 50, "label": "2", "popup": [
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" }
|
||||
] },
|
||||
{ "code": 51, "label": "3", "popup": [
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 190, "label": "¾" }
|
||||
] },
|
||||
{ "code": 52, "label": "4", "popup": [
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
] },
|
||||
{ "code": 53, "label": "5", "popup": [
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
] },
|
||||
{ "code": 54, "label": "6", "popup": [] },
|
||||
{ "code": 55, "label": "7", "popup": [
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
] },
|
||||
{ "code": 56, "label": "8", "popup": [] },
|
||||
{ "code": 57, "label": "9", "popup": [] },
|
||||
{ "code": 48, "label": "0", "popup": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
] }
|
||||
{ "code": 49, "label": "1", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 50, "label": "2", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 51, "label": "3", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 52, "label": "4", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 53, "label": "5", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
} },
|
||||
{ "code": 54, "label": "6", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
} },
|
||||
{ "code": 55, "label": "7", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
} },
|
||||
{ "code": 56, "label": "8", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
} },
|
||||
{ "code": 57, "label": "9", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
} },
|
||||
{ "code": 48, "label": "0", "type": "numeric", "popup": {
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,43 +1,53 @@
|
||||
{
|
||||
"type": "numeric_advanced",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 43, "label": "+", "popup": [
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 42, "label": "*" },
|
||||
{ "code": 47, "label": "/" }
|
||||
] },
|
||||
{ "code": 43, "label": "+", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 42, "label": "*" },
|
||||
{ "code": 47, "label": "/" }
|
||||
]
|
||||
} },
|
||||
{ "code": 49, "label": "1", "type": "numeric" },
|
||||
{ "code": 50, "label": "2", "type": "numeric" },
|
||||
{ "code": 51, "label": "3", "type": "numeric" },
|
||||
{ "code": 37, "label": "%", "popup": [] }
|
||||
], [
|
||||
{ "code": 40, "label": "(", "popup": [
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 123, "label": "{" }
|
||||
] },
|
||||
{ "code": 37, "label": "%" }
|
||||
],
|
||||
[
|
||||
{ "code": 40, "label": "(", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 123, "label": "{" }
|
||||
]
|
||||
} },
|
||||
{ "code": 52, "label": "4", "type": "numeric" },
|
||||
{ "code": 53, "label": "5", "type": "numeric" },
|
||||
{ "code": 54, "label": "6", "type": "numeric" },
|
||||
{ "code": 32, "label": " ", "popup": [] }
|
||||
], [
|
||||
{ "code": 41, "label": ")", "popup": [
|
||||
{ "code": 93, "label": "]" },
|
||||
{ "code": 125, "label": "}" }
|
||||
] },
|
||||
{ "code": 32, "label": "space" }
|
||||
],
|
||||
[
|
||||
{ "code": 41, "label": ")", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 93, "label": "]" },
|
||||
{ "code": 125, "label": "}" }
|
||||
]
|
||||
} },
|
||||
{ "code": 55, "label": "7", "type": "numeric" },
|
||||
{ "code": 56, "label": "8", "type": "numeric" },
|
||||
{ "code": 57, "label": "9", "type": "numeric" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -201, "label": "view_characters", "type": "system_gui" },
|
||||
{ "code": 44, "label": ",", "popup": [] },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 48, "label": "0", "type": "numeric" },
|
||||
{ "code": 61, "label": "=", "popup": [] },
|
||||
{ "code": 46, "label": ".", "popup": [] },
|
||||
{ "code": 61, "label": "=" },
|
||||
{ "code": 46, "label": "." },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
{
|
||||
"type": "numeric",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 49, "label": "1", "type": "numeric" },
|
||||
{ "code": 50, "label": "2", "type": "numeric" },
|
||||
{ "code": 51, "label": "3", "type": "numeric" },
|
||||
{ "code": 45, "label": "-", "popup": [] }
|
||||
], [
|
||||
{ "code": 45, "label": "-" }
|
||||
],
|
||||
[
|
||||
{ "code": 52, "label": "4", "type": "numeric" },
|
||||
{ "code": 53, "label": "5", "type": "numeric" },
|
||||
{ "code": 54, "label": "6", "type": "numeric" },
|
||||
{ "code": 32, "label": " ", "popup": [] }
|
||||
], [
|
||||
{ "code": 32, "label": "space" }
|
||||
],
|
||||
[
|
||||
{ "code": 55, "label": "7", "type": "numeric" },
|
||||
{ "code": 56, "label": "8", "type": "numeric" },
|
||||
{ "code": 57, "label": "9", "type": "numeric" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
{ "code": 44, "label": ",", "popup": [] },
|
||||
{ "code": 48, "label": "0", "type": "numeric", "popup": [
|
||||
{ "code": 43, "label": "+" }
|
||||
] },
|
||||
{ "code": 46, "label": ".", "popup": [] },
|
||||
],
|
||||
[
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 48, "label": "0", "type": "numeric", "popup": {
|
||||
"main": { "code": 43, "label": "+" }
|
||||
} },
|
||||
{ "code": 46, "label": "." },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
{
|
||||
"type": "phone",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 49, "label": "1", "type": "numeric" },
|
||||
{ "code": 50, "label": "2", "type": "numeric" },
|
||||
{ "code": 51, "label": "3", "type": "numeric" },
|
||||
{ "code": 45, "label": "-", "popup": [] }
|
||||
], [
|
||||
{ "code": 45, "label": "-" }
|
||||
],
|
||||
[
|
||||
{ "code": 52, "label": "4", "type": "numeric" },
|
||||
{ "code": 53, "label": "5", "type": "numeric" },
|
||||
{ "code": 54, "label": "6", "type": "numeric" },
|
||||
{ "code": 32, "label": " ", "popup": [] }
|
||||
], [
|
||||
{ "code": 32, "label": "space" }
|
||||
],
|
||||
[
|
||||
{ "code": 55, "label": "7", "type": "numeric" },
|
||||
{ "code": 56, "label": "8", "type": "numeric" },
|
||||
{ "code": 57, "label": "9", "type": "numeric" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -207, "label": "view_phone2", "type": "system_gui" },
|
||||
{ "code": 48, "label": "0", "type": "numeric", "popup": [
|
||||
{ "code": 43, "label": "+" }
|
||||
] },
|
||||
{ "code": 46, "label": ".", "popup": [] },
|
||||
{ "code": 48, "label": "0", "type": "numeric", "popup": {
|
||||
"main": { "code": 43, "label": "+" }
|
||||
} },
|
||||
{ "code": 46, "label": "." },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
{
|
||||
"type": "phone2",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 40, "label": "(", "popup": [] },
|
||||
{ "code": 47, "label": "/", "popup": [] },
|
||||
{ "code": 41, "label": ")", "popup": [] },
|
||||
{ "code": 45, "label": "-", "popup": [] }
|
||||
], [
|
||||
{ "code": 78, "label": "N", "popup": [] },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 45, "label": "-" }
|
||||
],
|
||||
[
|
||||
{ "code": 78, "label": "N" },
|
||||
{ "code": 44, "label": "pause", "type": "system_gui" },
|
||||
{ "code": 44, "label": ",", "popup": [] },
|
||||
{ "code": 32, "label": " ", "popup": [] }
|
||||
], [
|
||||
{ "code": 42, "label": "*", "popup": [] },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": 32, "label": "space" }
|
||||
],
|
||||
[
|
||||
{ "code": 42, "label": "*" },
|
||||
{ "code": 59, "label": "wait", "type": "system_gui" },
|
||||
{ "code": 35, "label": "#", "popup": [] },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -206, "label": "view_phone", "type": "system_gui" },
|
||||
{ "code": 43, "label": "+", "popup": [] },
|
||||
{ "code": 46, "label": ".", "popup": [] },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 46, "label": "." },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
{
|
||||
"type": "symbols/mod",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": -203, "label": "view_symbols2", "type": "system_gui" },
|
||||
{ "code": 0 },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -201, "label": "view_characters", "type": "system_gui" },
|
||||
{ "code": 44, "label": ",", "popup": [] },
|
||||
{ "code": 44, "label": "," },
|
||||
{ "code": -205, "label": "view_numeric_advanced", "type": "system_gui" },
|
||||
{ "code": 32, "label": " ", "popup": [] },
|
||||
{ "code": 46, "label": ".", "popup": [] },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 46, "label": ".", "popup": {
|
||||
"main": { "code": 8230, "label": "…" }
|
||||
} },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,71 +1,96 @@
|
||||
{
|
||||
"type": "symbols",
|
||||
"name": "western_default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 64, "label": "@", "popup": [] },
|
||||
{ "code": 35, "label": "#", "popup": [
|
||||
{ "code": 8470, "label": "№" }
|
||||
] },
|
||||
{ "code": 36, "label": "$", "popup": [
|
||||
{ "code": 8369, "label": "₱" },
|
||||
{ "code": 8364, "label": "€" },
|
||||
{ "code": 162, "label": "¢" },
|
||||
{ "code": 163, "label": "£" },
|
||||
{ "code": 165, "label": "¥" }
|
||||
] },
|
||||
{ "code": 95, "label": "_", "popup": [] },
|
||||
{ "code": 38, "label": "&", "popup": [] },
|
||||
{ "code": 45, "label": "-", "popup": [
|
||||
{ "code": 8212, "label": "—" },
|
||||
{ "code": 95, "label": "_" },
|
||||
{ "code": 8211, "label": "–" },
|
||||
{ "code": 183, "label": "·" }
|
||||
] },
|
||||
{ "code": 43, "label": "+", "popup": [
|
||||
{ "code": 177, "label": "±" }
|
||||
] },
|
||||
{ "code": 40, "label": "(", "popup": [
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 60, "label": "<" },
|
||||
{ "code": 123, "label": "{" }
|
||||
] },
|
||||
{ "code": 41, "label": ")", "popup": [
|
||||
{ "code": 93, "label": "]" },
|
||||
{ "code": 62, "label": ">" },
|
||||
{ "code": 125, "label": "}" }
|
||||
] },
|
||||
{ "code": 47, "label": "/", "popup": [] }
|
||||
], [
|
||||
{ "code": 42, "label": "*", "popup": [
|
||||
{ "code": 9733, "label": "★" },
|
||||
{ "code": 8224, "label": "†" },
|
||||
{ "code": 8225, "label": "‡" }
|
||||
] },
|
||||
{ "code": 34, "label": "\"", "popup": [
|
||||
{ "code": 8222, "label": "„" },
|
||||
{ "code": 8220, "label": "“" },
|
||||
{ "code": 8221, "label": "”" },
|
||||
{ "code": 171, "label": "«" },
|
||||
{ "code": 187, "label": "»" }
|
||||
] },
|
||||
{ "code": 39, "label": "'", "popup": [
|
||||
{ "code": 8218, "label": "‚" },
|
||||
{ "code": 8216, "label": "‘" },
|
||||
{ "code": 8217, "label": "’" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 8250, "label": "›" }
|
||||
] },
|
||||
{ "code": 58, "label": ":", "popup": [] },
|
||||
{ "code": 59, "label": ";", "popup": [] },
|
||||
{ "code": 33, "label": "!", "popup": [
|
||||
{ "code": 161, "label": "¡" }
|
||||
] },
|
||||
{ "code": 63, "label": "?", "popup": [
|
||||
{ "code": 191, "label": "¿" },
|
||||
{ "code": 8253, "label": "‽" }
|
||||
] }
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 35, "label": "#", "popup": {
|
||||
"main": { "code": 8470, "label": "№" }
|
||||
} },
|
||||
{ "code": 36, "label": "$", "popup": {
|
||||
"main": { "code": 8364, "label": "€" },
|
||||
"relevant": [
|
||||
{ "code": 8369, "label": "₱" },
|
||||
{ "code": 162, "label": "¢" },
|
||||
{ "code": 163, "label": "£" },
|
||||
{ "code": 165, "label": "¥" }
|
||||
]
|
||||
} },
|
||||
{ "code": 37, "label": "%", "popup": {
|
||||
"main": { "code": 8240, "label": "‰" },
|
||||
"relevant": [
|
||||
{ "code": 8453, "label": "℅" }
|
||||
]
|
||||
} },
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 45, "label": "-", "popup": {
|
||||
"main": { "code": 95, "label": "_" },
|
||||
"relevant": [
|
||||
{ "code": 8212, "label": "—" },
|
||||
{ "code": 8211, "label": "–" },
|
||||
{ "code": 183, "label": "·" }
|
||||
]
|
||||
} },
|
||||
{ "code": 43, "label": "+", "popup": {
|
||||
"main": { "code": 177, "label": "±" }
|
||||
} },
|
||||
{ "code": 40, "label": "(", "popup": {
|
||||
"main": { "code": 60, "label": "<" },
|
||||
"relevant": [
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 123, "label": "{" }
|
||||
]
|
||||
} },
|
||||
{ "code": 41, "label": ")", "popup": {
|
||||
"main": { "code": 62, "label": ">" },
|
||||
"relevant": [
|
||||
{ "code": 93, "label": "]" },
|
||||
{ "code": 125, "label": "}" }
|
||||
]
|
||||
} },
|
||||
{ "code": 47, "label": "/" }
|
||||
],
|
||||
[
|
||||
{ "code": 42, "label": "*", "popup": {
|
||||
"main": { "code": 8224, "label": "†" },
|
||||
"relevant": [
|
||||
{ "code": 9733, "label": "★" },
|
||||
{ "code": 8225, "label": "‡" }
|
||||
]
|
||||
} },
|
||||
{ "code": 34, "label": "\"", "popup": {
|
||||
"main": { "code": 8221, "label": "”" },
|
||||
"relevant": [
|
||||
{ "code": 8222, "label": "„" },
|
||||
{ "code": 8220, "label": "“" },
|
||||
{ "code": 171, "label": "«" },
|
||||
{ "code": 187, "label": "»" }
|
||||
]
|
||||
} },
|
||||
{ "code": 39, "label": "'", "popup": {
|
||||
"main": { "code": 8217, "label": "’" },
|
||||
"relevant": [
|
||||
{ "code": 8218, "label": "‚" },
|
||||
{ "code": 8216, "label": "‘" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 8250, "label": "›" }
|
||||
]
|
||||
} },
|
||||
{ "code": 58, "label": ":", "popup": {
|
||||
"main": { "code": 8942, "label": "⋮" }
|
||||
} },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 33, "label": "!", "popup": {
|
||||
"main": { "code": 161, "label": "¡" }
|
||||
} },
|
||||
{ "code": 63, "label": "?", "popup": {
|
||||
"main": { "code": 191, "label": "¿" },
|
||||
"relevant": [
|
||||
{ "code": 8253, "label": "‽" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
{
|
||||
"type": "symbols2/mod",
|
||||
"name": "default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "code": 0 },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -5, "label": "delete", "type": "enter_editing" }
|
||||
], [
|
||||
],
|
||||
[
|
||||
{ "code": -201, "label": "view_characters", "type": "system_gui" },
|
||||
{ "code": 60, "label": "<", "popup": [] },
|
||||
{ "code": 60, "label": "<", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 171, "label": "«" },
|
||||
{ "code": 8804, "label": "≤" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code":10216, "label": "⟨" }
|
||||
]
|
||||
} },
|
||||
{ "code": -205, "label": "view_numeric_advanced", "type": "system_gui" },
|
||||
{ "code": 32, "label": " ", "popup": [] },
|
||||
{ "code": 62, "label": ">", "popup": [] },
|
||||
{ "code": 10, "label": "enter", "type": "enter_editing" }
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "code": 62, "label": ">", "popup": {
|
||||
"relevant": [
|
||||
{ "code":10217, "label": "⟩" },
|
||||
{ "code": 8250, "label": "›" },
|
||||
{ "code": 8805, "label": "≥" },
|
||||
{ "code": 187, "label": "»" }
|
||||
]
|
||||
} },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,69 +1,79 @@
|
||||
{
|
||||
"type": "symbols2",
|
||||
"name": "western_default",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr",
|
||||
"arrangement": [
|
||||
[
|
||||
{ "code": 126, "label": "~", "popup": [] },
|
||||
{ "code": 96, "label": "`", "popup": [] },
|
||||
{ "code": 124, "label": "|", "popup": [] },
|
||||
{ "code": 8226, "label": "•", "popup": [
|
||||
{ "code": 9827, "label": "♣" },
|
||||
{ "code": 9824, "label": "♠" },
|
||||
{ "code": 9834, "label": "♪" },
|
||||
{ "code": 9829, "label": "♥" },
|
||||
{ "code": 9830, "label": "♦" }
|
||||
] },
|
||||
{ "code": 8730, "label": "√", "popup": [] },
|
||||
{ "code": 960, "label": "π", "popup": [
|
||||
{ "code": 937, "label": "Ω" },
|
||||
{ "code": 928, "label": "Π" },
|
||||
{ "code": 956, "label": "μ" }
|
||||
] },
|
||||
{ "code": 247, "label": "÷", "popup": [] },
|
||||
{ "code": 215, "label": "×", "popup": [] },
|
||||
{ "code": 182, "label": "¶", "popup": [
|
||||
{ "code": 167, "label": "§" }
|
||||
] },
|
||||
{ "code": 8710, "label": "∆", "popup": [] }
|
||||
], [
|
||||
{ "code": 163, "label": "£", "popup": [] },
|
||||
{ "code": 162, "label": "¢", "popup": [] },
|
||||
{ "code": 8364, "label": "€", "popup": [] },
|
||||
{ "code": 165, "label": "¥", "popup": [] },
|
||||
{ "code": 94, "label": "^", "popup": [
|
||||
{ "code": 8592, "label": "←" },
|
||||
{ "code": 8593, "label": "↑" },
|
||||
{ "code": 8595, "label": "↓" },
|
||||
{ "code": 8594, "label": "→" }
|
||||
] },
|
||||
{ "code": 176, "label": "°", "popup": [
|
||||
{ "code": 8242, "label": "′" },
|
||||
{ "code": 8243, "label": "″" }
|
||||
] },
|
||||
{ "code": 61, "label": "=", "popup": [
|
||||
{ "code": 8734, "label": "∞" },
|
||||
{ "code": 8800, "label": "≠" },
|
||||
{ "code": 8776, "label": "≈" }
|
||||
] },
|
||||
{ "code": 123, "label": "{", "popup": [
|
||||
{ "code": 40, "label": "(" }
|
||||
] },
|
||||
{ "code": 125, "label": "}", "popup": [
|
||||
{ "code": 41, "label": ")" }
|
||||
] },
|
||||
{ "code": 92, "label": "\\", "popup": [] }
|
||||
], [
|
||||
{ "code": 37, "label": "%", "popup": [
|
||||
{ "code": 8240, "label": "‰" },
|
||||
{ "code": 8453, "label": "℅" }
|
||||
] },
|
||||
{ "code": 169, "label": "©", "popup": [] },
|
||||
{ "code": 174, "label": "®", "popup": [] },
|
||||
{ "code": 8482, "label": "™", "popup": [] },
|
||||
{ "code": 10003, "label": "✓", "popup": [] },
|
||||
{ "code": 91, "label": "[", "popup": [] },
|
||||
{ "code": 93, "label": "]", "popup": [] }
|
||||
{ "code": 126, "label": "~" },
|
||||
{ "code": 96, "label": "`" },
|
||||
{ "code": 124, "label": "|" },
|
||||
{ "code": 8226, "label": "•", "popup": {
|
||||
"main": { "code": 9834, "label": "♪" },
|
||||
"relevant": [
|
||||
{ "code": 9827, "label": "♣" },
|
||||
{ "code": 9824, "label": "♠" },
|
||||
{ "code": 9829, "label": "♥" },
|
||||
{ "code": 9830, "label": "♦" }
|
||||
]
|
||||
} },
|
||||
{ "code": 8730, "label": "√" },
|
||||
{ "code": 960, "label": "π", "popup": {
|
||||
"main": { "code": 928, "label": "Π" },
|
||||
"relevant": [
|
||||
{ "code": 937, "label": "Ω" },
|
||||
{ "code": 956, "label": "μ" }
|
||||
]
|
||||
} },
|
||||
{ "code": 247, "label": "÷" },
|
||||
{ "code": 215, "label": "×" },
|
||||
{ "code": 182, "label": "¶", "popup": {
|
||||
"main": { "code": 167, "label": "§" }
|
||||
} },
|
||||
{ "code": 8710, "label": "∆" }
|
||||
],
|
||||
[
|
||||
{ "code": 163, "label": "£" },
|
||||
{ "code": 162, "label": "¢" },
|
||||
{ "code": 8364, "label": "€" },
|
||||
{ "code": 165, "label": "¥" },
|
||||
{ "code": 94, "label": "^", "popup": {
|
||||
"main": { "code": 8593, "label": "↑" },
|
||||
"relevant": [
|
||||
{ "code": 8592, "label": "←" },
|
||||
{ "code": 8595, "label": "↓" },
|
||||
{ "code": 8594, "label": "→" }
|
||||
]
|
||||
} },
|
||||
{ "code": 176, "label": "°", "popup": {
|
||||
"main": { "code": 8242, "label": "′" },
|
||||
"relevant": [
|
||||
{ "code": 8243, "label": "″" }
|
||||
]
|
||||
} },
|
||||
{ "code": 61, "label": "=", "popup": {
|
||||
"main": { "code": 8800, "label": "≠" },
|
||||
"relevant": [
|
||||
{ "code": 8734, "label": "∞" },
|
||||
{ "code": 8776, "label": "≈" }
|
||||
]
|
||||
} },
|
||||
{ "code": 123, "label": "{", "popup": {
|
||||
"main": { "code": 40, "label": "(" }
|
||||
} },
|
||||
{ "code": 125, "label": "}", "popup": {
|
||||
"main": { "code": 41, "label": ")" }
|
||||
} },
|
||||
{ "code": 92, "label": "\\" }
|
||||
],
|
||||
[
|
||||
{ "code": 95, "label": "_" },
|
||||
{ "code": 169, "label": "©" },
|
||||
{ "code": 174, "label": "®" },
|
||||
{ "code": 8482, "label": "™" },
|
||||
{ "code": 10003, "label": "✓" },
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 93, "label": "]" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,60 +1,64 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "floris_day",
|
||||
"displayName": "Floris Day",
|
||||
"author": "patrickgold",
|
||||
"label": "Floris Day",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNightTheme": false,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "true",
|
||||
"semiTransparentColor": "#20000000",
|
||||
"textColor": "#000000"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#E0E0E0"
|
||||
"background": "#E0E0E0"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#FFFFFF",
|
||||
"bgColorPressed": "#F5F5F5",
|
||||
"fgColor": "@window/textColor"
|
||||
"background": "#FFFFFF",
|
||||
"backgroundPressed": "#F5F5F5",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "true"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#EEEEEE",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#757575"
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#757575"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#E8F5E9"
|
||||
"background": "#E8F5E9",
|
||||
"foreground": "#424242"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#424242"
|
||||
"popup": {
|
||||
"background": "#EEEEEE",
|
||||
"backgroundActive": "#BDBDBD",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#8A8A8A"
|
||||
"background": "transparent",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#8A8A8A"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
"background": "@key/background",
|
||||
"foreground": "@key/foreground"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
app/src/main/assets/ime/theme/floris_day_borderless.json
Normal file
68
app/src/main/assets/ime/theme/floris_day_borderless.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "floris_day_borderless",
|
||||
"label": "Floris Day Borderless",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNightTheme": false,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "true",
|
||||
"semiTransparentColor": "#20000000",
|
||||
"textColor": "#000000"
|
||||
},
|
||||
"keyboard": {
|
||||
"background": "#E0E0E0"
|
||||
},
|
||||
"key": {
|
||||
"background": "transparent",
|
||||
"backgroundPressed": "#7FF5F5F5",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "false"
|
||||
},
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"key:space": {
|
||||
"background": "#7FF5F5F5",
|
||||
"backgroundPressed": "#FFF5F5F5"
|
||||
},
|
||||
"media": {
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#757575"
|
||||
},
|
||||
"oneHanded": {
|
||||
"background": "#E8F5E9",
|
||||
"foreground": "#424242"
|
||||
},
|
||||
"popup": {
|
||||
"background": "#EEEEEE",
|
||||
"backgroundActive": "#BDBDBD",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"background": "transparent",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#8A8A8A"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"background": "#FFFFFF",
|
||||
"foreground": "@window/textColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,64 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "floris_night",
|
||||
"displayName": "Floris Night",
|
||||
"author": "patrickgold",
|
||||
"label": "Floris Night",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNightTheme": true,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/bgColor",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "false",
|
||||
"semiTransparentColor": "#20FFFFFF",
|
||||
"textColor": "#FFFFFF"
|
||||
},
|
||||
"keyboard": {
|
||||
"bgColor": "#212121"
|
||||
"background": "#212121"
|
||||
},
|
||||
"key": {
|
||||
"bgColor": "#424242",
|
||||
"bgColorPressed": "#616161",
|
||||
"fgColor": "@window/textColor"
|
||||
"background": "#424242",
|
||||
"backgroundPressed": "#616161",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "true"
|
||||
},
|
||||
"keyEnter": {
|
||||
"bgColor": "@window/colorPrimary",
|
||||
"bgColorPressed": "@window/colorPrimaryDark",
|
||||
"fgColor": "#FFFFFF"
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"keyPopup": {
|
||||
"bgColor": "#757575",
|
||||
"bgColorActive": "#BDBDBD",
|
||||
"fgColor": "@window/textColor"
|
||||
},
|
||||
"keyShift": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"bgColorPressed": "@key/bgColorPressed",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorCapsLock": "@window/colorAccent"
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#BDBDBD"
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#BDBDBD"
|
||||
},
|
||||
"oneHanded": {
|
||||
"bgColor": "#1B5E20"
|
||||
"background": "#1B5E20",
|
||||
"foreground": "#EEEEEE"
|
||||
},
|
||||
"oneHandedButton": {
|
||||
"fgColor": "#EEEEEE"
|
||||
"popup": {
|
||||
"background": "#757575",
|
||||
"backgroundActive": "#BDBDBD",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"bgColor": "transparent",
|
||||
"fgColor": "@window/textColor",
|
||||
"fgColorAlt": "#73FFFFFF"
|
||||
"background": "transparent",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#73FFFFFF"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"bgColor": "@key/bgColor",
|
||||
"fgColor": "@key/fgColor"
|
||||
"background": "@key/background",
|
||||
"foreground": "@key/foreground"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
app/src/main/assets/ime/theme/floris_night_borderless.json
Normal file
68
app/src/main/assets/ime/theme/floris_night_borderless.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "floris_night_borderless",
|
||||
"label": "Floris Night Borderless",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNightTheme": true,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#4CAF50",
|
||||
"colorPrimaryDark": "#388E3C",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "false",
|
||||
"semiTransparentColor": "#20FFFFFF",
|
||||
"textColor": "#FFFFFF"
|
||||
},
|
||||
"keyboard": {
|
||||
"background": "#212121"
|
||||
},
|
||||
"key": {
|
||||
"background": "transparent",
|
||||
"backgroundPressed": "#7F616161",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "false"
|
||||
},
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"key:space": {
|
||||
"background": "#2F616161",
|
||||
"backgroundPressed": "#7F616161"
|
||||
},
|
||||
"media": {
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#BDBDBD"
|
||||
},
|
||||
"oneHanded": {
|
||||
"background": "#1B5E20",
|
||||
"foreground": "#EEEEEE"
|
||||
},
|
||||
"popup": {
|
||||
"background": "#757575",
|
||||
"backgroundActive": "#BDBDBD",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"background": "transparent",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#73FFFFFF"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"background": "#424242",
|
||||
"foreground": "@window/textColor"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,30 @@ limitations under the License.
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>ExpandableFab</h3>
|
||||
<span>Copyright (c) 2020 Kelvin Abumere and The Nambi Company</span>
|
||||
<pre>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>FlexboxLayout</h3>
|
||||
<span>Copyright 2018 Google LLC</span>
|
||||
<pre>
|
||||
@@ -479,6 +503,24 @@ limitations under the License.
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>kotlin-result</h3>
|
||||
<span>Copyright (c) 2017-2020 Michael Bull (https://www.michael-bull.com)</span>
|
||||
<pre>
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Material Icons</h3>
|
||||
<span>Copyright 2018 Google LLC</span>
|
||||
<pre>
|
||||
@@ -696,6 +738,24 @@ You may obtain a copy of the License at
|
||||
|
||||
http://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.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Timber</h3>
|
||||
<span>Copyright 2013 Jake Wharton</span>
|
||||
<pre>
|
||||
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
|
||||
|
||||
http://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.
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.crashutility
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.databinding.CrashDialogBinding
|
||||
|
||||
class CrashDialogActivity : AppCompatActivity() {
|
||||
private lateinit var binding: CrashDialogBinding
|
||||
private var stacktrace: String = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = CrashDialogBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
stacktrace = CrashUtility.getUnhandledStacktrace(this)
|
||||
binding.stacktrace.text = stacktrace
|
||||
|
||||
binding.copyToClipboard.setOnClickListener {
|
||||
val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE)
|
||||
if (clipboardManager != null && clipboardManager is ClipboardManager) {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(stacktrace, stacktrace))
|
||||
}
|
||||
}
|
||||
|
||||
binding.openBugReportForm.setOnClickListener {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse(resources.getString(R.string.florisboard__issue_tracker_new_issue_url))
|
||||
)
|
||||
startActivity(browserIntent)
|
||||
}
|
||||
|
||||
binding.close.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.crashutility
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.*
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.util.Log
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Abstract class which holds several static methods used for handling unexpected errors.
|
||||
*
|
||||
* Parts of this class (especially the install() function and the uncaughtException() handler) have
|
||||
* been inspired by the great CustomActivityOnCrash library:
|
||||
* https://github.com/Ereza/CustomActivityOnCrash (licensed under Apache 2.0)
|
||||
* https://github.com/Ereza/CustomActivityOnCrash/blob/master/library/src/main/java/cat/ereza/customactivityoncrash/CustomActivityOnCrash.java
|
||||
*/
|
||||
abstract class CrashUtility private constructor() {
|
||||
companion object {
|
||||
private const val SHARED_PREFS_FILE = "crash_utility"
|
||||
private const val SHARED_PREFS_LAST_CRASH_TIMESTAMP = "last_crash_timestamp"
|
||||
|
||||
private const val NOTIFICATION_CHANNEL_ID = "dev.patrickgold.florisboard.crashutility"
|
||||
private const val NOTIFICATION_ID = 0xFBAD0100
|
||||
|
||||
private const val UNHANDLED_STACKTRACE_FILE_EXT = "stacktrace"
|
||||
|
||||
private var lastActivityCreated: WeakReference<Activity?> = WeakReference(null)
|
||||
|
||||
/**
|
||||
* Installs the CrashUtility crash handler for the given package [context]. Also registers
|
||||
* a notification channel for devices with Android 8.0+.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function does
|
||||
* nothing.
|
||||
* @return True if the installation was successful, false otherwise.
|
||||
*/
|
||||
fun install(context: Context?): Boolean {
|
||||
if (context == null) {
|
||||
Timber.e(
|
||||
"install($context): Can't install crash handler with a null Context object, doing nothing!"
|
||||
)
|
||||
return false
|
||||
}
|
||||
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
if (oldHandler is UncaughtExceptionHandler) {
|
||||
Timber.i("install($context): Crash handler is already installed, doing nothing!")
|
||||
} else {
|
||||
val application = context.applicationContext
|
||||
if (application != null && application is Application) {
|
||||
try {
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
UncaughtExceptionHandler(
|
||||
WeakReference(application),
|
||||
WeakReference(oldHandler),
|
||||
application.filesDir.absolutePath
|
||||
)
|
||||
)
|
||||
Timber.i(
|
||||
"install($context): Successfully installed crash handler for this application!"
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e(
|
||||
"install($context): Failed to install crash handler, probably due to missing runtime permission 'setDefaultUncaughtExceptionHandler':\n$e"
|
||||
)
|
||||
return false
|
||||
} catch (e: Exception) {
|
||||
Timber.e(
|
||||
"install($context): Failed to install crash handler due to an unspecified error:\n$e"
|
||||
)
|
||||
return false
|
||||
}
|
||||
application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
|
||||
override fun onActivityCreated(
|
||||
activity: Activity,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
if (activity !is CrashDialogActivity) {
|
||||
lastActivityCreated = WeakReference(activity)
|
||||
}
|
||||
}
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
override fun onActivityResumed(activity: Activity) {}
|
||||
override fun onActivityPaused(activity: Activity) {}
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
override fun onActivitySaveInstanceState(
|
||||
activity: Activity,
|
||||
outState: Bundle
|
||||
) {}
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
})
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
try {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
if (notificationManager != null && notificationManager is NotificationManager) {
|
||||
val notificationChannel = NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
context.resources.getString(R.string.crash_notification_channel__title),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
}
|
||||
Timber.i(
|
||||
"install($context): Successfully created crash handler notification channel!"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(
|
||||
"install($context): Failed to create crash handler notification channel due to an unspecified error:\n$e"
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.e(
|
||||
"install($context): Can't install crash handler with a null Application object, doing nothing!"
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns all unhandled stacktrace files.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function returns
|
||||
* an empty string.
|
||||
* @return All unhandled stacktrace files or an empty string.
|
||||
*/
|
||||
fun getUnhandledStacktrace(context: Context?): String {
|
||||
context ?: return ""
|
||||
val retString: StringBuilder = StringBuilder()
|
||||
val ustDir = getUstDir(context)
|
||||
if (ustDir.isDirectory) {
|
||||
(ustDir.listFiles { pathname ->
|
||||
pathname.name.endsWith(".$UNHANDLED_STACKTRACE_FILE_EXT")
|
||||
})?.forEach { file ->
|
||||
val newLine = System.lineSeparator()
|
||||
Timber.i("Reading unhandled stacktrace: ${file.name}")
|
||||
retString.append("~~~ ${file.name} ~~~$newLine$newLine")
|
||||
retString.append(readFile(file))
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
return retString.toString()
|
||||
}
|
||||
|
||||
fun hasUnhandledStacktraceFiles(context: Context): Boolean {
|
||||
val ustDir = getUstDir(context)
|
||||
return if (ustDir.isDirectory) {
|
||||
(ustDir.listFiles { pathname ->
|
||||
pathname.name.endsWith(".$UNHANDLED_STACKTRACE_FILE_EXT")
|
||||
})?.isNotEmpty() ?: false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last crash timestamp from the shared preferences.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function returns
|
||||
* the default value for the timestamp (0).
|
||||
* @return The last time crash timestamp or 0.
|
||||
*/
|
||||
private fun getLastCrashTimestamp(context: Context?): Long {
|
||||
context ?: return 0
|
||||
return context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
|
||||
.getLong(SHARED_PREFS_LAST_CRASH_TIMESTAMP, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last crash timestamp in the shared preferences.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function does
|
||||
* nothing.
|
||||
* @param value The timestamp of the current crash.
|
||||
*/
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun setLastCrashTimestamp(context: Context?, value: Long) {
|
||||
context ?: return
|
||||
// Note: must use commit() instead of apply(), as the value must be immediately written
|
||||
// to be possibly instantly read again.
|
||||
context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putLong(SHARED_PREFS_LAST_CRASH_TIMESTAMP, value)
|
||||
.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the current unhandled stacktrace directory.
|
||||
*
|
||||
* @param context The current package context.
|
||||
* @return The File object for the directory.
|
||||
*/
|
||||
private fun getUstDir(context: Context): File {
|
||||
val path = context.filesDir.absolutePath
|
||||
return File(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the stacktrace file for given [timestamp].
|
||||
*
|
||||
* @param context The current package context.
|
||||
* @param timestamp The timestamp of the stacktrace file to get.
|
||||
* @return The File object for the stacktrace file.
|
||||
*/
|
||||
private fun getUstFile(context: Context, timestamp: Long): File {
|
||||
val path = context.filesDir.absolutePath
|
||||
return File("$path/$timestamp.$UNHANDLED_STACKTRACE_FILE_EXT")
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a notification which opens [CrashDialogActivity] with given parameters.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function does
|
||||
* nothing.
|
||||
* @param id The ID of the notification.
|
||||
* @param title The title of the notification.
|
||||
* @param body The body of the notification.
|
||||
*/
|
||||
private fun pushNotification(context: Context?, id: Int, title: String, body: String) {
|
||||
context ?: return
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
if (notificationManager != null && notificationManager is NotificationManager) {
|
||||
val notificationBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Notification.Builder(context.applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
Notification.Builder(context.applicationContext).apply {
|
||||
setPriority(Notification.PRIORITY_MAX)
|
||||
}
|
||||
}
|
||||
val crashDialogIntent = Intent(context, CrashDialogActivity::class.java)
|
||||
val notification = notificationBuilder.run {
|
||||
setContentTitle(title)
|
||||
style = Notification.BigTextStyle().bigText(body)
|
||||
setContentText(body)
|
||||
setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
setContentIntent(PendingIntent.getActivity(context, 0, crashDialogIntent, 0)).setAutoCancel(
|
||||
true
|
||||
)
|
||||
build()
|
||||
}
|
||||
notificationManager.notify(id, notification)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a notification configured for a single crash.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function does
|
||||
* nothing.
|
||||
*/
|
||||
private fun pushCrashOnceNotification(context: Context?) {
|
||||
context ?: return
|
||||
pushNotification(
|
||||
context,
|
||||
NOTIFICATION_ID.toInt(),
|
||||
context.resources.getString(R.string.crash_once_notification__title),
|
||||
context.resources.getString(R.string.crash_once_notification__body)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a notification configured for multiple crashes.
|
||||
*
|
||||
* @param context The current package context. If null is supplied, this function does
|
||||
* nothing.
|
||||
*/
|
||||
private fun pushCrashMultipleNotification(context: Context?) {
|
||||
context ?: return
|
||||
pushNotification(
|
||||
context,
|
||||
NOTIFICATION_ID.toInt(),
|
||||
context.resources.getString(R.string.crash_multiple_notification__title),
|
||||
context.resources.getString(R.string.crash_multiple_notification__body)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a given [file] and returns its content.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun readFile(file: File): String {
|
||||
val retText = StringBuilder()
|
||||
if (file.exists()) {
|
||||
val newLine = System.lineSeparator()
|
||||
file.forEachLine {
|
||||
retText.append(it)
|
||||
retText.append(newLine)
|
||||
}
|
||||
}
|
||||
return retText.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes given [text] to given [file]. If the file already exists, its current content
|
||||
* will be overwritten.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @param text The text to write to the file.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun writeToFile(file: File, text: String) {
|
||||
try {
|
||||
file.writeText(text)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom UncaughtExceptionHandler, which writes the captured stacktrace of the crash to the
|
||||
* internal storage, pushes a crash notification and kills the current process.
|
||||
*/
|
||||
class UncaughtExceptionHandler(
|
||||
private val application: WeakReference<Application>,
|
||||
private val oldHandler: WeakReference<Thread.UncaughtExceptionHandler?>,
|
||||
private val path: String
|
||||
) : Thread.UncaughtExceptionHandler {
|
||||
override fun uncaughtException(thread: Thread?, throwable: Throwable?) {
|
||||
Timber.e("Detected application crash, executing custom crash handler.")
|
||||
thread ?: return
|
||||
throwable ?: return
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val stacktrace = Log.getStackTraceString(throwable)
|
||||
val ustFile = File("$path/$timestamp.$UNHANDLED_STACKTRACE_FILE_EXT")
|
||||
writeToFile(ustFile, stacktrace)
|
||||
val application = application.get()
|
||||
if (application != null) {
|
||||
val lastTimestamp = getLastCrashTimestamp(application)
|
||||
if (lastTimestamp > 0) {
|
||||
val lastFile = getUstFile(application, lastTimestamp)
|
||||
val lastStacktrace = readFile(lastFile)
|
||||
if (lastStacktrace == stacktrace) {
|
||||
// Delete last stacktrace if it matches previous unhandled one
|
||||
lastFile.delete()
|
||||
}
|
||||
}
|
||||
setLastCrashTimestamp(application, timestamp)
|
||||
if (timestamp - lastTimestamp < 5000) {
|
||||
pushCrashMultipleNotification(application)
|
||||
val florisboard = FlorisBoard.getInstanceOrNull()
|
||||
if (florisboard != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
florisboard.switchToPreviousInputMethod()
|
||||
} else {
|
||||
val imm = application.getSystemService(Context.INPUT_METHOD_SERVICE)
|
||||
if (imm != null && imm is InputMethodManager) {
|
||||
@Suppress("DEPRECATION")
|
||||
imm.switchToNextInputMethod(
|
||||
florisboard.window?.window?.attributes?.token,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pushCrashOnceNotification(application)
|
||||
}
|
||||
}
|
||||
val lastActivity = lastActivityCreated.get()
|
||||
if (lastActivity != null) {
|
||||
//oldHandler.get()?.uncaughtException(thread, throwable)
|
||||
lastActivity.finish()
|
||||
lastActivityCreated.clear()
|
||||
}
|
||||
Process.killProcess(Process.myPid())
|
||||
exitProcess(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.app.Application
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.crashutility.CrashUtility
|
||||
import dev.patrickgold.florisboard.ime.extension.AssetManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import timber.log.Timber
|
||||
|
||||
class FlorisApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
CrashUtility.install(this)
|
||||
val prefHelper = PrefHelper.getDefaultInstance(this)
|
||||
val assetManager = AssetManager.init(this)
|
||||
ThemeManager.init(this, assetManager, prefHelper)
|
||||
prefHelper.initDefaultPreferences()
|
||||
}
|
||||
}
|
||||
@@ -22,28 +22,33 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.inputmethodservice.InputMethodService
|
||||
import android.media.AudioManager
|
||||
import android.os.*
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.CursorAnchorInfo
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputConnection
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import com.squareup.moshi.Json
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.media.MediaInputManager
|
||||
import dev.patrickgold.florisboard.ime.popup.PopupLayerView
|
||||
import dev.patrickgold.florisboard.ime.text.TextInputManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.settings.SettingsMainActivity
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
import timber.log.Timber
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Variable which holds the current [FlorisBoard] instance. To get this instance from another
|
||||
@@ -55,7 +60,8 @@ private var florisboardInstance: FlorisBoard? = null
|
||||
* Core class responsible to link together both the text and media input managers as well as
|
||||
* managing the one-handed UI.
|
||||
*/
|
||||
class FlorisBoard : InputMethodService() {
|
||||
class FlorisBoard : InputMethodService(), ClipboardManager.OnPrimaryClipChangedListener,
|
||||
ThemeManager.OnThemeUpdatedListener {
|
||||
lateinit var prefs: PrefHelper
|
||||
private set
|
||||
|
||||
@@ -63,18 +69,25 @@ class FlorisBoard : InputMethodService() {
|
||||
get() = inputWindowView?.context ?: this
|
||||
var inputView: InputView? = null
|
||||
private set
|
||||
var popupLayerView: PopupLayerView? = null
|
||||
private set
|
||||
private var inputWindowView: InputWindowView? = null
|
||||
private var eventListeners: MutableList<EventListener> = mutableListOf()
|
||||
private var eventListeners: MutableList<WeakReference<EventListener?>?> = mutableListOf()
|
||||
|
||||
private var audioManager: AudioManager? = null
|
||||
private var imeManager:InputMethodManager? = null
|
||||
var clipboardManager: ClipboardManager? = null
|
||||
private val themeManager: ThemeManager = ThemeManager.default()
|
||||
private var vibrator: Vibrator? = null
|
||||
private val osHandler = Handler()
|
||||
|
||||
var activeEditorInstance: EditorInstance = EditorInstance.default()
|
||||
|
||||
lateinit var subtypeManager: SubtypeManager
|
||||
lateinit var activeSubtype: Subtype
|
||||
private var currentThemeIsNight: Boolean = false
|
||||
private var currentThemeResId: Int = 0
|
||||
private var isNumberRowVisible: Boolean = false
|
||||
|
||||
val textInputManager: TextInputManager
|
||||
val mediaInputManager: MediaInputManager
|
||||
@@ -88,14 +101,22 @@ class FlorisBoard : InputMethodService() {
|
||||
|
||||
companion object {
|
||||
private const val IME_ID: String = "dev.patrickgold.florisboard/.ime.core.FlorisBoard"
|
||||
private const val IME_ID_DEBUG: String = "dev.patrickgold.florisboard.debug/dev.patrickgold.florisboard.ime.core.FlorisBoard"
|
||||
|
||||
fun checkIfImeIsEnabled(context: Context): Boolean {
|
||||
val activeImeIds = Settings.Secure.getString(
|
||||
context.contentResolver,
|
||||
Settings.Secure.ENABLED_INPUT_METHODS
|
||||
)
|
||||
if (BuildConfig.DEBUG) Log.i(FlorisBoard::class.simpleName, "List of active IMEs: $activeImeIds")
|
||||
return activeImeIds.split(":").contains(IME_ID)
|
||||
Timber.i("List of active IMEs: $activeImeIds")
|
||||
return when {
|
||||
BuildConfig.DEBUG -> {
|
||||
activeImeIds.split(":").contains(IME_ID_DEBUG)
|
||||
}
|
||||
else -> {
|
||||
activeImeIds.split(":").contains(IME_ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIfImeIsSelected(context: Context): Boolean {
|
||||
@@ -103,8 +124,15 @@ class FlorisBoard : InputMethodService() {
|
||||
context.contentResolver,
|
||||
Settings.Secure.DEFAULT_INPUT_METHOD
|
||||
)
|
||||
if (BuildConfig.DEBUG) Log.i(FlorisBoard::class.simpleName, "Selected IME: $selectedImeId")
|
||||
return selectedImeId == IME_ID
|
||||
Timber.i("Selected IME: $selectedImeId")
|
||||
return when {
|
||||
BuildConfig.DEBUG -> {
|
||||
selectedImeId == IME_ID_DEBUG
|
||||
}
|
||||
else -> {
|
||||
selectedImeId == IME_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@@ -144,10 +172,12 @@ class FlorisBoard : InputMethodService() {
|
||||
.build()
|
||||
)
|
||||
}
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onCreate()")
|
||||
Timber.i("onCreate()")
|
||||
|
||||
imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboardManager?.addPrimaryClipChangedListener(this)
|
||||
vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
prefs = PrefHelper.getDefaultInstance(this)
|
||||
prefs.initDefaultPreferences()
|
||||
@@ -155,89 +185,123 @@ class FlorisBoard : InputMethodService() {
|
||||
subtypeManager = SubtypeManager(this, prefs)
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
|
||||
currentThemeIsNight = prefs.internal.themeCurrentIsNight
|
||||
currentThemeIsNight = themeManager.activeTheme.isNightTheme
|
||||
currentThemeResId = getDayNightBaseThemeId(currentThemeIsNight)
|
||||
isNumberRowVisible = prefs.keyboard.numberRow
|
||||
setTheme(currentThemeResId)
|
||||
updateTheme()
|
||||
themeManager.registerOnThemeUpdatedListener(this)
|
||||
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
|
||||
super.onCreate()
|
||||
eventListeners.toList().forEach { it.onCreate() }
|
||||
eventListeners.toList().forEach { it?.get()?.onCreate() }
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateInputView(): View? {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onCreateInputView()")
|
||||
Timber.i("onCreateInputView()")
|
||||
|
||||
baseContext.setTheme(currentThemeResId)
|
||||
|
||||
inputWindowView = layoutInflater.inflate(R.layout.florisboard, null) as InputWindowView
|
||||
popupLayerView = inputWindowView?.findViewById(R.id.popup_layer)
|
||||
|
||||
eventListeners.toList().forEach { it.onCreateInputView() }
|
||||
eventListeners.toList().forEach { it?.get()?.onCreateInputView() }
|
||||
|
||||
return inputWindowView
|
||||
}
|
||||
|
||||
fun registerInputView(inputView: InputView) {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "registerInputView(inputView)")
|
||||
Timber.i("registerInputView($inputView)")
|
||||
|
||||
this.inputView = inputView
|
||||
initializeOneHandedEnvironment()
|
||||
updateTheme()
|
||||
updateSoftInputWindowLayoutParameters()
|
||||
updateOneHandedPanelVisibility()
|
||||
themeManager.notifyCallbackReceivers()
|
||||
setActiveInput(R.id.text_input)
|
||||
|
||||
eventListeners.toList().forEach { it.onRegisterInputView(inputView) }
|
||||
eventListeners.toList().forEach { it?.get()?.onRegisterInputView(inputView) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onDestroy()")
|
||||
Timber.i("onDestroy()")
|
||||
|
||||
themeManager.unregisterOnThemeUpdatedListener(this)
|
||||
clipboardManager?.removePrimaryClipChangedListener(this)
|
||||
osHandler.removeCallbacksAndMessages(null)
|
||||
florisboardInstance = null
|
||||
|
||||
eventListeners.toList().forEach { it.onDestroy() }
|
||||
eventListeners.toList().forEach { it?.get()?.onDestroy() }
|
||||
eventListeners.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) {
|
||||
Timber.i("onStartInput($attribute, $restarting)")
|
||||
|
||||
super.onStartInput(attribute, restarting)
|
||||
currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE)
|
||||
}
|
||||
|
||||
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR)
|
||||
Timber.i("onStartInputView($info, $restarting)")
|
||||
Timber.i("onStartInputView: ${info?.debugSummarize()}")
|
||||
|
||||
super.onStartInputView(info, restarting)
|
||||
eventListeners.toList().forEach { it.onStartInputView(info, restarting) }
|
||||
activeEditorInstance = EditorInstance.from(info, this)
|
||||
themeManager.updateRemoteColorValues(activeEditorInstance.packageName)
|
||||
eventListeners.toList().forEach {
|
||||
it?.get()?.onStartInputView(activeEditorInstance, restarting)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinishInputView(finishingInput: Boolean) {
|
||||
currentInputConnection?.requestCursorUpdates(0)
|
||||
Timber.i( "onFinishInputView($finishingInput)")
|
||||
|
||||
if (finishingInput) {
|
||||
activeEditorInstance = EditorInstance.default()
|
||||
}
|
||||
|
||||
super.onFinishInputView(finishingInput)
|
||||
eventListeners.toList().forEach { it.onFinishInputView(finishingInput) }
|
||||
eventListeners.toList().forEach { it?.get()?.onFinishInputView(finishingInput) }
|
||||
}
|
||||
|
||||
override fun onFinishInput() {
|
||||
Timber.i("onFinishInput()")
|
||||
|
||||
super.onFinishInput()
|
||||
currentInputConnection?.requestCursorUpdates(0)
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowShown()")
|
||||
Timber.i("onWindowShown()")
|
||||
|
||||
prefs.sync()
|
||||
updateTheme()
|
||||
val newIsNumberRowVisible = prefs.keyboard.numberRow
|
||||
if (isNumberRowVisible != newIsNumberRowVisible) {
|
||||
textInputManager.layoutManager.clearLayoutCache(KeyboardMode.CHARACTERS)
|
||||
isNumberRowVisible = newIsNumberRowVisible
|
||||
}
|
||||
themeManager.update()
|
||||
updateOneHandedPanelVisibility()
|
||||
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
setActiveInput(R.id.text_input)
|
||||
|
||||
super.onWindowShown()
|
||||
eventListeners.toList().forEach { it.onWindowShown() }
|
||||
eventListeners.toList().forEach { it?.get()?.onWindowShown() }
|
||||
}
|
||||
|
||||
override fun onWindowHidden() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onWindowHidden()")
|
||||
Timber.i("onWindowHidden()")
|
||||
|
||||
super.onWindowHidden()
|
||||
eventListeners.toList().forEach { it.onWindowHidden() }
|
||||
eventListeners.toList().forEach { it?.get()?.onWindowHidden() }
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
Timber.i("onConfigurationChanged($newConfig)")
|
||||
if (isInputViewShown) {
|
||||
updateOneHandedPanelVisibility()
|
||||
}
|
||||
@@ -245,73 +309,73 @@ class FlorisBoard : InputMethodService() {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {
|
||||
super.onUpdateCursorAnchorInfo(cursorAnchorInfo)
|
||||
eventListeners.toList().forEach { it.onUpdateCursorAnchorInfo(cursorAnchorInfo) }
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
oldSelEnd: Int,
|
||||
newSelStart: Int,
|
||||
newSelEnd: Int,
|
||||
candidatesStart: Int,
|
||||
candidatesEnd: Int
|
||||
oldSelStart: Int, oldSelEnd: Int,
|
||||
newSelStart: Int, newSelEnd: Int,
|
||||
candidatesStart: Int, candidatesEnd: Int
|
||||
) {
|
||||
Timber.i("onUpdateSelection($oldSelStart, $oldSelEnd, $newSelStart, $newSelEnd, $candidatesStart, $candidatesEnd)")
|
||||
|
||||
super.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
oldSelStart, oldSelEnd,
|
||||
newSelStart, newSelEnd,
|
||||
candidatesStart, candidatesEnd
|
||||
)
|
||||
eventListeners.toList().forEach {
|
||||
it.onUpdateSelection(
|
||||
oldSelStart,
|
||||
oldSelEnd,
|
||||
newSelStart,
|
||||
newSelEnd,
|
||||
candidatesStart,
|
||||
candidatesEnd
|
||||
)
|
||||
}
|
||||
activeEditorInstance.onUpdateSelection(
|
||||
oldSelStart, oldSelEnd,
|
||||
newSelStart, newSelEnd
|
||||
)
|
||||
eventListeners.toList().forEach { it?.get()?.onUpdateSelection() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Reapplies the supplies colors and settings from prefs to navigation bar.
|
||||
*/
|
||||
private fun updateTheme() {
|
||||
val newThemeIsNightMode = prefs.internal.themeCurrentIsNight
|
||||
override fun onThemeUpdated(theme: Theme) {
|
||||
// Rebuild the UI if the theme has changed from day to night or vice versa to prevent
|
||||
// theme glitches with scrollbars and hints of buttons in the media UI. If the UI must be
|
||||
// rebuild, quit this method, as it will be called again by the newly created UI.
|
||||
val newThemeIsNightMode = theme.isNightTheme
|
||||
if (currentThemeIsNight != newThemeIsNightMode) {
|
||||
currentThemeResId = getDayNightBaseThemeId(newThemeIsNightMode)
|
||||
currentThemeIsNight = newThemeIsNightMode
|
||||
setInputView(onCreateInputView())
|
||||
return
|
||||
}
|
||||
|
||||
// Get Window and the flags of the DecorView
|
||||
val w = window?.window ?: return
|
||||
inputView?.setBackgroundColor(prefs.theme.keyboardBgColor)
|
||||
w.navigationBarColor = prefs.theme.navBarColor
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
|
||||
// Update navigation bar theme
|
||||
w.navigationBarColor = theme.getAttr(Theme.Attr.WINDOW_NAVIGATION_BAR_COLOR).toSolidColor().color
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
var flags = w.decorView.systemUiVisibility
|
||||
flags = if (prefs.theme.navBarIsLight) {
|
||||
flags = if (theme.getAttr(Theme.Attr.WINDOW_NAVIGATION_BAR_LIGHT).toOnOff().state) {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
} else {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
}
|
||||
w.decorView.systemUiVisibility = flags
|
||||
}
|
||||
inputView?.oneHandedCtrlPanelStart?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.oneHandedCtrlPanelEnd?.setBackgroundColor(prefs.theme.oneHandedBgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_start)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_end)
|
||||
?.imageTintList = ColorStateList.valueOf(prefs.theme.oneHandedButtonFgColor)
|
||||
eventListeners.toList().forEach { it.onApplyThemeAttributes() }
|
||||
|
||||
// Update status bar to be transparent
|
||||
// Done as starting with Android 11 the IME Window takes the primaryColorDark value and
|
||||
// colors the status bar, which isn't the desired behavior. (See issue #43)
|
||||
flags = flags or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
w.statusBarColor = Color.TRANSPARENT
|
||||
|
||||
// Apply the new flags to the DecorView
|
||||
w.decorView.systemUiVisibility = flags
|
||||
|
||||
// Update InputView theme
|
||||
inputView?.setBackgroundColor(theme.getAttr(Theme.Attr.KEYBOARD_BACKGROUND).toSolidColor().color)
|
||||
inputView?.oneHandedCtrlPanelStart?.setBackgroundColor(theme.getAttr(Theme.Attr.ONE_HANDED_BACKGROUND).toSolidColor().color)
|
||||
inputView?.oneHandedCtrlPanelEnd?.setBackgroundColor(theme.getAttr(Theme.Attr.ONE_HANDED_BACKGROUND).toSolidColor().color)
|
||||
ColorStateList.valueOf(theme.getAttr(Theme.Attr.ONE_HANDED_FOREGROUND).toSolidColor().color).also {
|
||||
inputView?.oneHandedCtrlMoveStart?.imageTintList = it
|
||||
inputView?.oneHandedCtrlMoveEnd?.imageTintList = it
|
||||
inputView?.oneHandedCtrlCloseStart?.imageTintList = it
|
||||
inputView?.oneHandedCtrlCloseEnd?.imageTintList = it
|
||||
}
|
||||
eventListeners.toList().forEach { it?.get()?.onApplyThemeAttributes() }
|
||||
}
|
||||
|
||||
override fun onComputeInsets(outInsets: Insets?) {
|
||||
@@ -398,6 +462,20 @@ class FlorisBoard : InputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given [SwipeAction]. Ignores any [SwipeAction] but the ones relevant for this
|
||||
* class.
|
||||
*/
|
||||
fun executeSwipeAction(swipeAction: SwipeAction) {
|
||||
when (swipeAction) {
|
||||
SwipeAction.HIDE_KEYBOARD -> requestHideSelf(0)
|
||||
SwipeAction.SWITCH_TO_PREV_SUBTYPE -> switchToPrevSubtype()
|
||||
SwipeAction.SWITCH_TO_NEXT_SUBTYPE -> switchToNextSubtype()
|
||||
SwipeAction.SWITCH_TO_PREV_KEYBOARD -> switchToPrevKeyboard()
|
||||
else -> textInputManager.executeSwipeAction(swipeAction)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the IME and launches [SettingsMainActivity].
|
||||
*/
|
||||
@@ -417,6 +495,43 @@ class FlorisBoard : InputMethodService() {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
fun switchToPrevKeyboard(){
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
switchToPreviousInputMethod()
|
||||
} else {
|
||||
window.window?.let { window ->
|
||||
@Suppress("DEPRECATION")
|
||||
imeManager?.switchToLastInputMethod(window.attributes.token)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e,"Unable to switch to the previous IME")
|
||||
imeManager?.showInputMethodPicker()
|
||||
}
|
||||
}
|
||||
|
||||
fun switchToNextKeyboard(){
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
switchToNextInputMethod(false)
|
||||
} else {
|
||||
window.window?.let { window ->
|
||||
@Suppress("DEPRECATION")
|
||||
imeManager?.switchToNextInputMethod(window.attributes.token, false)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e,"Unable to switch to the next IME")
|
||||
imeManager?.showInputMethodPicker()
|
||||
}
|
||||
}
|
||||
|
||||
fun switchToPrevSubtype() {
|
||||
activeSubtype = subtypeManager.switchToPrevSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
}
|
||||
|
||||
fun switchToNextSubtype() {
|
||||
activeSubtype = subtypeManager.switchToNextSubtype() ?: Subtype.DEFAULT
|
||||
onSubtypeChanged(activeSubtype)
|
||||
@@ -430,25 +545,21 @@ class FlorisBoard : InputMethodService() {
|
||||
fun setActiveInput(type: Int) {
|
||||
when (type) {
|
||||
R.id.text_input -> {
|
||||
inputView?.mainViewFlipper?.displayedChild =
|
||||
inputView?.mainViewFlipper?.indexOfChild(textInputManager.textViewGroup) ?: 0
|
||||
inputView?.mainViewFlipper?.displayedChild = 0
|
||||
}
|
||||
R.id.media_input -> {
|
||||
inputView?.mainViewFlipper?.displayedChild =
|
||||
inputView?.mainViewFlipper?.indexOfChild(mediaInputManager.mediaViewGroup) ?: 0
|
||||
inputView?.mainViewFlipper?.displayedChild = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeOneHandedEnvironment() {
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_start)
|
||||
?.setOnClickListener { v -> onOneHandedPanelButtonClick(v) }
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_move_end)
|
||||
?.setOnClickListener { v -> onOneHandedPanelButtonClick(v) }
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_start)
|
||||
?.setOnClickListener { v -> onOneHandedPanelButtonClick(v) }
|
||||
inputView?.findViewById<ImageButton>(R.id.one_handed_ctrl_close_end)
|
||||
?.setOnClickListener { v -> onOneHandedPanelButtonClick(v) }
|
||||
{ v:View -> onOneHandedPanelButtonClick(v) }.also {
|
||||
inputView?.oneHandedCtrlMoveStart?.setOnClickListener(it)
|
||||
inputView?.oneHandedCtrlMoveEnd?.setOnClickListener(it)
|
||||
inputView?.oneHandedCtrlCloseStart?.setOnClickListener(it)
|
||||
inputView?.oneHandedCtrlCloseEnd?.setOnClickListener(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOneHandedPanelButtonClick(v: View) {
|
||||
@@ -505,25 +616,36 @@ class FlorisBoard : InputMethodService() {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
override fun onPrimaryClipChanged() {
|
||||
eventListeners.toList().forEach { it?.get()?.onPrimaryClipChanged() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a given [listener] to the list which will receive FlorisBoard events.
|
||||
*
|
||||
* @param listener The listener object which receives the events.
|
||||
* @returns True if the listener has been added successfully, false otherwise.
|
||||
* @return True if the listener has been added successfully, false otherwise.
|
||||
*/
|
||||
fun addEventListener(listener: EventListener): Boolean {
|
||||
return eventListeners.add(listener)
|
||||
return eventListeners.add(WeakReference(listener))
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given [listener] from the list which will receive FlorisBoard events.
|
||||
*
|
||||
* TODO: implement this function with a proper iterator
|
||||
*
|
||||
* @param listener The same listener object which was used in [addEventListener].
|
||||
* @returns True if the listener has been removed successfully, false otherwise. A false return
|
||||
* @return True if the listener has been removed successfully, false otherwise. A false return
|
||||
* value may also indicate that the [listener] was not added previously.
|
||||
*/
|
||||
fun removeEventListener(listener: EventListener): Boolean {
|
||||
return eventListeners.remove(listener)
|
||||
eventListeners.toList().forEach {
|
||||
if (it?.get() == listener) {
|
||||
return eventListeners.remove(it)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
interface EventListener {
|
||||
@@ -532,23 +654,16 @@ class FlorisBoard : InputMethodService() {
|
||||
fun onRegisterInputView(inputView: InputView) {}
|
||||
fun onDestroy() {}
|
||||
|
||||
fun onStartInputView(info: EditorInfo?, restarting: Boolean) {}
|
||||
fun onStartInputView(instance: EditorInstance, restarting: Boolean) {}
|
||||
fun onFinishInputView(finishingInput: Boolean) {}
|
||||
|
||||
fun onWindowShown() {}
|
||||
fun onWindowHidden() {}
|
||||
|
||||
fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo?) {}
|
||||
fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
oldSelEnd: Int,
|
||||
newSelStart: Int,
|
||||
newSelEnd: Int,
|
||||
candidatesStart: Int,
|
||||
candidatesEnd: Int
|
||||
) {}
|
||||
fun onUpdateSelection() {}
|
||||
|
||||
fun onApplyThemeAttributes() {}
|
||||
fun onPrimaryClipChanged() {}
|
||||
fun onSubtypeChanged(newSubtype: Subtype) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,17 @@ package dev.patrickgold.florisboard.ime.core
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.util.ViewLayoutUtils
|
||||
import timber.log.Timber
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@@ -33,13 +39,13 @@ class InputView : LinearLayout {
|
||||
private var florisboard: FlorisBoard = FlorisBoard.getInstance()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
var desiredInputViewHeight: Int = resources.getDimension(R.dimen.inputView_baseHeight).roundToInt()
|
||||
var desiredInputViewHeight: Float = resources.getDimension(R.dimen.inputView_baseHeight)
|
||||
private set
|
||||
var desiredSmartbarHeight: Int = resources.getDimension(R.dimen.smartbar_baseHeight).roundToInt()
|
||||
var desiredSmartbarHeight: Float = resources.getDimension(R.dimen.smartbar_baseHeight)
|
||||
private set
|
||||
var desiredTextKeyboardViewHeight: Int = resources.getDimension(R.dimen.textKeyboardView_baseHeight).roundToInt()
|
||||
var desiredTextKeyboardViewHeight: Float = resources.getDimension(R.dimen.textKeyboardView_baseHeight)
|
||||
private set
|
||||
var desiredMediaKeyboardViewHeight: Int = resources.getDimension(R.dimen.mediaKeyboardView_baseHeight).roundToInt()
|
||||
var desiredMediaKeyboardViewHeight: Float = resources.getDimension(R.dimen.mediaKeyboardView_baseHeight)
|
||||
private set
|
||||
|
||||
var mainViewFlipper: ViewFlipper? = null
|
||||
@@ -48,26 +54,42 @@ class InputView : LinearLayout {
|
||||
private set
|
||||
var oneHandedCtrlPanelEnd: LinearLayout? = null
|
||||
private set
|
||||
var oneHandedCtrlMoveStart: ImageButton? = null
|
||||
private set
|
||||
var oneHandedCtrlMoveEnd: ImageButton? = null
|
||||
private set
|
||||
var oneHandedCtrlCloseStart: ImageButton? = null
|
||||
private set
|
||||
var oneHandedCtrlCloseEnd: ImageButton? = null
|
||||
private set
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onAttachedToWindow()")
|
||||
Timber.i("onAttachedToWindow()")
|
||||
|
||||
super.onAttachedToWindow()
|
||||
|
||||
mainViewFlipper = findViewById(R.id.main_view_flipper)
|
||||
oneHandedCtrlPanelStart = findViewById(R.id.one_handed_ctrl_panel_start)
|
||||
oneHandedCtrlPanelEnd = findViewById(R.id.one_handed_ctrl_panel_end)
|
||||
oneHandedCtrlMoveStart = findViewById(R.id.one_handed_ctrl_move_start)
|
||||
oneHandedCtrlMoveEnd = findViewById(R.id.one_handed_ctrl_move_end)
|
||||
oneHandedCtrlCloseStart = findViewById(R.id.one_handed_ctrl_close_start)
|
||||
oneHandedCtrlCloseEnd = findViewById(R.id.one_handed_ctrl_close_end)
|
||||
|
||||
florisboard.registerInputView(this)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val heightFactor = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 0.85f
|
||||
Configuration.ORIENTATION_LANDSCAPE -> 1.0f
|
||||
else -> if (prefs.keyboard.oneHandedMode != "off") {
|
||||
0.9f
|
||||
} else {
|
||||
@@ -81,14 +103,71 @@ class InputView : LinearLayout {
|
||||
"mid_tall" -> 1.05f
|
||||
"tall" -> 1.10f
|
||||
"extra_tall" -> 1.15f
|
||||
"custom" -> prefs.keyboard.heightFactorCustom.toFloat() / 100.0f
|
||||
else -> 1.00f
|
||||
}
|
||||
val height = (resources.getDimension(R.dimen.inputView_baseHeight) * heightFactor).roundToInt()
|
||||
desiredInputViewHeight = height
|
||||
desiredSmartbarHeight = (0.16129 * height).roundToInt()
|
||||
desiredTextKeyboardViewHeight = height - desiredSmartbarHeight
|
||||
desiredMediaKeyboardViewHeight = height
|
||||
var baseHeight = calcInputViewHeight() * heightFactor
|
||||
var baseSmartbarHeight = 0.16129f * baseHeight
|
||||
var baseTextInputHeight = baseHeight - baseSmartbarHeight
|
||||
val tim = florisboard.textInputManager
|
||||
val shouldGiveAdditionalSpace = prefs.keyboard.numberRow &&
|
||||
!(tim.getActiveKeyboardMode() == KeyboardMode.NUMERIC ||
|
||||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE ||
|
||||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE2)
|
||||
if (shouldGiveAdditionalSpace) {
|
||||
val additionalHeight = desiredTextKeyboardViewHeight * 0.18f
|
||||
baseHeight += additionalHeight
|
||||
baseTextInputHeight += additionalHeight
|
||||
}
|
||||
val smartbarDisabled = !prefs.smartbar.enabled ||
|
||||
tim.keyVariation == KeyVariation.PASSWORD && prefs.keyboard.numberRow ||
|
||||
tim.getActiveKeyboardMode() == KeyboardMode.NUMERIC ||
|
||||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE ||
|
||||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE2
|
||||
if (smartbarDisabled) {
|
||||
baseHeight = baseTextInputHeight
|
||||
baseSmartbarHeight = 0.0f
|
||||
}
|
||||
desiredInputViewHeight = baseHeight
|
||||
desiredSmartbarHeight = baseSmartbarHeight
|
||||
desiredTextKeyboardViewHeight = baseTextInputHeight
|
||||
desiredMediaKeyboardViewHeight = baseHeight
|
||||
// Add bottom offset for curved screens here. As the desired heights have already been set,
|
||||
// adding a value to the height now will result in a bottom padding (aka offset).
|
||||
baseHeight += ViewLayoutUtils.convertDpToPixel(
|
||||
florisboard.prefs.keyboard.bottomOffset.toFloat(),
|
||||
context
|
||||
)
|
||||
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(baseHeight.roundToInt(), MeasureSpec.EXACTLY))
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the input view height based on the current screen dimensions and the auto
|
||||
* selected dimension values.
|
||||
*
|
||||
* This method and the fraction values have been inspired by [OpenBoard](https://github.com/dslul/openboard)
|
||||
* but are not 1:1 the same. This implementation differs from the
|
||||
* [original](https://github.com/dslul/openboard/blob/90ae4c8aec034a8935e1fd02b441be25c7dba6ce/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ResourceUtils.java)
|
||||
* by calculating the average of the min and max height values, then taking at least the input
|
||||
* view base height and return this resulting value.
|
||||
*/
|
||||
private fun calcInputViewHeight(): Float {
|
||||
val dm: DisplayMetrics = resources.displayMetrics
|
||||
val minBaseSize: Float = when (resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> resources.getFraction(
|
||||
R.fraction.inputView_minHeightFraction, dm.heightPixels, dm.heightPixels
|
||||
)
|
||||
else -> resources.getFraction(
|
||||
R.fraction.inputView_minHeightFraction, dm.widthPixels, dm.widthPixels
|
||||
)
|
||||
}
|
||||
val maxBaseSize: Float = resources.getFraction(
|
||||
R.fraction.inputView_maxHeightFraction, dm.heightPixels, dm.heightPixels
|
||||
)
|
||||
return ((minBaseSize + maxBaseSize) / 2.0f).coerceAtLeast(
|
||||
resources.getDimension(R.dimen.inputView_baseHeight)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ import android.content.SharedPreferences
|
||||
import android.provider.Settings
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.DistanceThreshold
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.VelocityThreshold
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.SwitchKeyMode
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.util.TimeUtil
|
||||
import dev.patrickgold.florisboard.util.VersionName
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
@@ -37,9 +44,12 @@ class PrefHelper(
|
||||
|
||||
val advanced = Advanced(this)
|
||||
val correction = Correction(this)
|
||||
val gestures = Gestures(this)
|
||||
val glide = Glide(this)
|
||||
val internal = Internal(this)
|
||||
val keyboard = Keyboard(this)
|
||||
val localization = Localization(this)
|
||||
val smartbar = Smartbar(this)
|
||||
val suggestion = Suggestion(this)
|
||||
val theme = Theme(this)
|
||||
|
||||
@@ -132,6 +142,8 @@ class PrefHelper(
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_keyboard, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_theme, true)
|
||||
PreferenceManager.setDefaultValues(context, R.xml.prefs_typing, true)
|
||||
//theme.dayThemeRef = "assets:ime/theme/floris_day.json"
|
||||
//theme.nightThemeRef = "assets:ime/theme/floris_night.json"
|
||||
//setPref(Keyboard.SUBTYPES, "")
|
||||
//setPref(Internal.IS_IME_SET_UP, false)
|
||||
}
|
||||
@@ -160,6 +172,7 @@ class PrefHelper(
|
||||
companion object {
|
||||
const val SETTINGS_THEME = "advanced__settings_theme"
|
||||
const val SHOW_APP_ICON = "advanced__show_app_icon"
|
||||
const val FORCE_PRIVATE_MODE = "advanced__force_private_mode"
|
||||
}
|
||||
|
||||
var settingsTheme: String = ""
|
||||
@@ -168,6 +181,9 @@ class PrefHelper(
|
||||
var showAppIcon: Boolean = false
|
||||
get() = prefHelper.getPref(SHOW_APP_ICON, true)
|
||||
private set
|
||||
var forcePrivateMode: Boolean
|
||||
get() = prefHelper.getPref(FORCE_PRIVATE_MODE, false)
|
||||
set(v) = prefHelper.setPref(FORCE_PRIVATE_MODE, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,12 +191,90 @@ class PrefHelper(
|
||||
*/
|
||||
class Correction(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val DOUBLE_SPACE_PERIOD = "correction__double_space_period"
|
||||
const val AUTO_CAPITALIZATION = "correction__auto_capitalization"
|
||||
const val DOUBLE_SPACE_PERIOD = "correction__double_space_period"
|
||||
const val REMEMBER_CAPS_LOCK_STATE = "correction__remember_caps_lock_state"
|
||||
}
|
||||
|
||||
var doubleSpacePeriod: Boolean = false
|
||||
get() = prefHelper.getPref(DOUBLE_SPACE_PERIOD, true)
|
||||
private set
|
||||
var autoCapitalization: Boolean
|
||||
get() = prefHelper.getPref(AUTO_CAPITALIZATION, true)
|
||||
set(v) = prefHelper.setPref(AUTO_CAPITALIZATION, v)
|
||||
var doubleSpacePeriod: Boolean
|
||||
get() = prefHelper.getPref(DOUBLE_SPACE_PERIOD, true)
|
||||
set(v) = prefHelper.setPref(DOUBLE_SPACE_PERIOD, v)
|
||||
var rememberCapsLockState: Boolean
|
||||
get() = prefHelper.getPref(REMEMBER_CAPS_LOCK_STATE, false)
|
||||
set(v) = prefHelper.setPref(REMEMBER_CAPS_LOCK_STATE, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for gestures preferences.
|
||||
*/
|
||||
class Gestures(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val SWIPE_UP = "gestures__swipe_up"
|
||||
const val SWIPE_DOWN = "gestures__swipe_down"
|
||||
const val SWIPE_LEFT = "gestures__swipe_left"
|
||||
const val SWIPE_RIGHT = "gestures__swipe_right"
|
||||
const val SPACE_BAR_LONG_PRESS = "gestures__space_bar_long_press"
|
||||
const val SPACE_BAR_SWIPE_LEFT = "gestures__space_bar_swipe_left"
|
||||
const val SPACE_BAR_SWIPE_RIGHT = "gestures__space_bar_swipe_right"
|
||||
const val SPACE_BAR_SWIPE_UP = "gestures__space_bar_swipe_up"
|
||||
const val DELETE_KEY_SWIPE_LEFT = "gestures__delete_key_swipe_left"
|
||||
const val SWIPE_VELOCITY_THRESHOLD = "gestures__swipe_velocity_threshold"
|
||||
const val SWIPE_DISTANCE_THRESHOLD = "gestures__swipe_distance_threshold"
|
||||
}
|
||||
|
||||
var swipeUp: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_UP, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_UP, v)
|
||||
var swipeDown: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_DOWN, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_DOWN, v)
|
||||
var swipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_LEFT, v)
|
||||
var swipeRight: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SWIPE_RIGHT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SWIPE_RIGHT, v)
|
||||
var spaceBarLongPress: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_LONG_PRESS, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_LONG_PRESS, v)
|
||||
var spaceBarSwipeUp: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_SWIPE_UP, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_SWIPE_UP, v)
|
||||
var spaceBarSwipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_SWIPE_LEFT, v)
|
||||
var spaceBarSwipeRight: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(SPACE_BAR_SWIPE_RIGHT, "no_action"))
|
||||
set(v) = prefHelper.setPref(SPACE_BAR_SWIPE_RIGHT, v)
|
||||
var deleteKeySwipeLeft: SwipeAction
|
||||
get() = SwipeAction.fromString(prefHelper.getPref(DELETE_KEY_SWIPE_LEFT, "no_action"))
|
||||
set(v) = prefHelper.setPref(DELETE_KEY_SWIPE_LEFT, v)
|
||||
var swipeVelocityThreshold: VelocityThreshold
|
||||
get() = VelocityThreshold.fromString(prefHelper.getPref(SWIPE_VELOCITY_THRESHOLD, "normal"))
|
||||
set(v) = prefHelper.setPref(SWIPE_VELOCITY_THRESHOLD, v)
|
||||
var swipeDistanceThreshold: DistanceThreshold
|
||||
get() = DistanceThreshold.fromString(prefHelper.getPref(SWIPE_DISTANCE_THRESHOLD, "normal"))
|
||||
set(v) = prefHelper.setPref(SWIPE_DISTANCE_THRESHOLD, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for glide preferences.
|
||||
*/
|
||||
class Glide(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "glide__enabled"
|
||||
const val SHOW_TRAIL = "glide__show_trail"
|
||||
}
|
||||
|
||||
var enabled: Boolean
|
||||
get() = prefHelper.getPref(ENABLED, false)
|
||||
set(v) = prefHelper.setPref(ENABLED, v)
|
||||
var showTrail: Boolean
|
||||
get() = prefHelper.getPref(SHOW_TRAIL, false)
|
||||
set(v) = prefHelper.setPref(SHOW_TRAIL, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,9 +284,6 @@ class PrefHelper(
|
||||
class Internal(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val IS_IME_SET_UP = "internal__is_ime_set_up"
|
||||
const val THEME_CURRENT_BASED_ON = "internal__theme_current_based_on"
|
||||
const val THEME_CURRENT_IS_MODIFIED = "internal__theme_current_is_modified"
|
||||
const val THEME_CURRENT_IS_NIGHT = "internal__theme_current_is_night"
|
||||
const val VERSION_ON_INSTALL = "internal__version_on_install"
|
||||
const val VERSION_LAST_USE = "internal__version_last_use"
|
||||
const val VERSION_LAST_CHANGELOG = "internal__version_last_changelog"
|
||||
@@ -201,15 +292,6 @@ class PrefHelper(
|
||||
var isImeSetUp: Boolean
|
||||
get() = prefHelper.getPref(IS_IME_SET_UP, false)
|
||||
set(v) = prefHelper.setPref(IS_IME_SET_UP, v)
|
||||
var themeCurrentBasedOn: String
|
||||
get() = prefHelper.getPref(THEME_CURRENT_BASED_ON, "undefined")
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_BASED_ON, v)
|
||||
var themeCurrentIsModified: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_MODIFIED, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_MODIFIED, v)
|
||||
var themeCurrentIsNight: Boolean
|
||||
get() = prefHelper.getPref(THEME_CURRENT_IS_NIGHT, false)
|
||||
set(v) = prefHelper.setPref(THEME_CURRENT_IS_NIGHT, v)
|
||||
var versionOnInstall: String
|
||||
get() = prefHelper.getPref(VERSION_ON_INSTALL, VersionName.DEFAULT_RAW)
|
||||
set(v) = prefHelper.setPref(VERSION_ON_INSTALL, v)
|
||||
@@ -226,22 +308,51 @@ class PrefHelper(
|
||||
*/
|
||||
class Keyboard(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val HEIGHT_FACTOR = "keyboard__height_factor"
|
||||
const val LONG_PRESS_DELAY = "keyboard__long_press_delay"
|
||||
const val ONE_HANDED_MODE = "keyboard__one_handed_mode"
|
||||
const val POPUP_ENABLED = "keyboard__popup_enabled"
|
||||
const val SOUND_ENABLED = "keyboard__sound_enabled"
|
||||
const val SOUND_VOLUME = "keyboard__sound_volume"
|
||||
const val VIBRATION_ENABLED = "keyboard__vibration_enabled"
|
||||
const val VIBRATION_STRENGTH = "keyboard__vibration_strength"
|
||||
const val BOTTOM_OFFSET = "keyboard__bottom_offset"
|
||||
const val FONT_SIZE_MULTIPLIER_PORTRAIT = "keyboard__font_size_multiplier_portrait"
|
||||
const val FONT_SIZE_MULTIPLIER_LANDSCAPE = "keyboard__font_size_multiplier_landscape"
|
||||
const val HEIGHT_FACTOR = "keyboard__height_factor"
|
||||
const val HEIGHT_FACTOR_CUSTOM = "keyboard__height_factor_custom"
|
||||
const val HINTED_NUMBER_ROW_MODE = "keyboard__hinted_number_row_mode"
|
||||
const val HINTED_SYMBOLS_MODE = "keyboard__hinted_symbols_mode"
|
||||
const val LONG_PRESS_DELAY = "keyboard__long_press_delay"
|
||||
const val NUMBER_ROW = "keyboard__number_row"
|
||||
const val ONE_HANDED_MODE = "keyboard__one_handed_mode"
|
||||
const val POPUP_ENABLED = "keyboard__popup_enabled"
|
||||
const val SOUND_ENABLED = "keyboard__sound_enabled"
|
||||
const val SOUND_VOLUME = "keyboard__sound_volume"
|
||||
const val SWITCH_KEY_MODE = "keyboard__switch_key_mode"
|
||||
const val VIBRATION_ENABLED = "keyboard__vibration_enabled"
|
||||
const val VIBRATION_STRENGTH = "keyboard__vibration_strength"
|
||||
}
|
||||
|
||||
var bottomOffset: Int = 0
|
||||
get() = prefHelper.getPref(BOTTOM_OFFSET, 0)
|
||||
private set
|
||||
var fontSizeMultiplierPortrait: Int
|
||||
get() = prefHelper.getPref(FONT_SIZE_MULTIPLIER_PORTRAIT, 100)
|
||||
set(v) = prefHelper.setPref(FONT_SIZE_MULTIPLIER_PORTRAIT, v)
|
||||
var fontSizeMultiplierLandscape: Int
|
||||
get() = prefHelper.getPref(FONT_SIZE_MULTIPLIER_LANDSCAPE, 100)
|
||||
set(v) = prefHelper.setPref(FONT_SIZE_MULTIPLIER_LANDSCAPE, v)
|
||||
var heightFactor: String = ""
|
||||
get() = prefHelper.getPref(HEIGHT_FACTOR, "normal")
|
||||
private set
|
||||
var heightFactorCustom: Int
|
||||
get() = prefHelper.getPref(HEIGHT_FACTOR_CUSTOM, 100)
|
||||
set(v) = prefHelper.setPref(HEIGHT_FACTOR_CUSTOM, v)
|
||||
var hintedNumberRowMode: KeyHintMode
|
||||
get() = KeyHintMode.fromString(prefHelper.getPref(HINTED_NUMBER_ROW_MODE, KeyHintMode.ENABLED_ACCENT_PRIORITY.toString()))
|
||||
set(v) = prefHelper.setPref(HINTED_NUMBER_ROW_MODE, v)
|
||||
var hintedSymbolsMode: KeyHintMode
|
||||
get() = KeyHintMode.fromString(prefHelper.getPref(HINTED_SYMBOLS_MODE, KeyHintMode.ENABLED_ACCENT_PRIORITY.toString()))
|
||||
set(v) = prefHelper.setPref(HINTED_SYMBOLS_MODE, v)
|
||||
var longPressDelay: Int = 0
|
||||
get() = prefHelper.getPref(LONG_PRESS_DELAY, 300)
|
||||
private set
|
||||
var numberRow: Boolean
|
||||
get() = prefHelper.getPref(NUMBER_ROW, false)
|
||||
set(v) = prefHelper.setPref(NUMBER_ROW, v)
|
||||
var oneHandedMode: String
|
||||
get() = prefHelper.getPref(ONE_HANDED_MODE, "off")
|
||||
set(value) = prefHelper.setPref(ONE_HANDED_MODE, value)
|
||||
@@ -255,6 +366,9 @@ class PrefHelper(
|
||||
var soundVolume: Int = 0
|
||||
get() = prefHelper.getPref(SOUND_VOLUME, -1)
|
||||
private set
|
||||
var switchKeyMode: SwitchKeyMode
|
||||
get() = SwitchKeyMode.fromString(prefHelper.getPref(SWITCH_KEY_MODE, SwitchKeyMode.DYNAMIC_LANGUAGE_EMOJI.toString()))
|
||||
set(v) = prefHelper.setPref(SWITCH_KEY_MODE, v)
|
||||
var vibrationEnabled: Boolean = false
|
||||
get() = prefHelper.getPref(VIBRATION_ENABLED, true)
|
||||
private set
|
||||
@@ -274,32 +388,45 @@ class PrefHelper(
|
||||
}
|
||||
|
||||
var activeSubtypeId: Int
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
get() = prefHelper.getPref(ACTIVE_SUBTYPE_ID, Subtype.DEFAULT.id)
|
||||
set(v) = prefHelper.setPref(ACTIVE_SUBTYPE_ID, v)
|
||||
var subtypes: String
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
get() = prefHelper.getPref(SUBTYPES, "")
|
||||
set(v) = prefHelper.setPref(SUBTYPES, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for Smartbar preferences.
|
||||
*/
|
||||
class Smartbar(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "smartbar__enabled"
|
||||
}
|
||||
|
||||
var enabled: Boolean
|
||||
get() = prefHelper.getPref(ENABLED, true)
|
||||
set(v) = prefHelper.setPref(ENABLED, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for suggestion preferences.
|
||||
*/
|
||||
class Suggestion(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val ENABLED = "suggestion__enabled"
|
||||
const val SHOW_INSTEAD = "suggestion__show_instead"
|
||||
const val USE_PREV_WORDS = "suggestion__use_prev_words"
|
||||
const val ENABLED = "suggestion__enabled"
|
||||
const val SUGGEST_CLIPBOARD_CONTENT = "suggestion__suggest_clipboard_content"
|
||||
const val USE_PREV_WORDS = "suggestion__use_prev_words"
|
||||
}
|
||||
|
||||
var enabled: Boolean = false
|
||||
get() = prefHelper.getPref(ENABLED, true)
|
||||
private set
|
||||
var showInstead: String = ""
|
||||
get() = prefHelper.getPref(SHOW_INSTEAD, "number_row")
|
||||
private set
|
||||
var usePrevWords: Boolean = false
|
||||
get() = prefHelper.getPref(USE_PREV_WORDS, true)
|
||||
private set
|
||||
var enabled: Boolean
|
||||
get() = prefHelper.getPref(ENABLED, true)
|
||||
set(v) = prefHelper.setPref(ENABLED, v)
|
||||
var suggestClipboardContent: Boolean
|
||||
get() = prefHelper.getPref(SUGGEST_CLIPBOARD_CONTENT, false)
|
||||
set(v) = prefHelper.setPref(SUGGEST_CLIPBOARD_CONTENT, v)
|
||||
var usePrevWords: Boolean
|
||||
get() = prefHelper.getPref(USE_PREV_WORDS, true)
|
||||
set(v) = prefHelper.setPref(USE_PREV_WORDS, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,119 +434,35 @@ class PrefHelper(
|
||||
*/
|
||||
class Theme(private val prefHelper: PrefHelper) {
|
||||
companion object {
|
||||
const val COLOR_PRIMARY = "theme__colorPrimary"
|
||||
const val COLOR_PRIMARY_DARK = "theme__colorPrimaryDark"
|
||||
const val COLOR_ACCENT = "theme__colorAccent"
|
||||
const val NAV_BAR_COLOR = "theme__navBarColor"
|
||||
const val NAV_BAR_IS_LIGHT = "theme__navBarIsLight"
|
||||
const val KEYBOARD_BG_COLOR = "theme__keyboard_bgColor"
|
||||
const val KEY_BG_COLOR = "theme__key_bgColor"
|
||||
const val KEY_BG_COLOR_PRESSED = "theme__key_bgColorPressed"
|
||||
const val KEY_FG_COLOR = "theme__key_fgColor"
|
||||
const val KEY_ENTER_BG_COLOR = "theme__keyEnter_bgColor"
|
||||
const val KEY_ENTER_BG_COLOR_PRESSED = "theme__keyEnter_bgColorPressed"
|
||||
const val KEY_ENTER_FG_COLOR = "theme__keyEnter_fgColor"
|
||||
const val KEY_SHIFT_BG_COLOR = "theme__keyShift_bgColor"
|
||||
const val KEY_SHIFT_BG_COLOR_PRESSED = "theme__keyShift_bgColorPressed"
|
||||
const val KEY_SHIFT_FG_COLOR = "theme__keyShift_fgColor"
|
||||
const val KEY_SHIFT_FG_COLOR_CAPSLOCK = "theme__keyShift_fgColorCapsLock"
|
||||
const val KEY_POPUP_BG_COLOR = "theme__keyPopup_bgColor"
|
||||
const val KEY_POPUP_BG_COLOR_ACTIVE = "theme__keyPopup_bgColorActive"
|
||||
const val KEY_POPUP_FG_COLOR = "theme__keyPopup_fgColor"
|
||||
const val MEDIA_FG_COLOR = "theme__media_fgColor"
|
||||
const val MEDIA_FG_COLOR_ALT = "theme__media_fgColorAlt"
|
||||
const val ONE_HANDED_BG_COLOR = "theme__oneHanded_bgColor"
|
||||
const val ONE_HANDED_BUTTON_FG_COLOR = "theme__oneHandedButton_fgColor"
|
||||
const val SMARTBAR_BG_COLOR = "theme__smartbar_bgColor"
|
||||
const val SMARTBAR_FG_COLOR = "theme__smartbar_fgColor"
|
||||
const val SMARTBAR_FG_COLOR_ALT = "theme__smartbar_fgColorAlt"
|
||||
const val SMARTBAR_BUTTON_BG_COLOR = "theme__smartbarButton_bgColor"
|
||||
const val SMARTBAR_BUTTON_FG_COLOR = "theme__smartbarButton_fgColor"
|
||||
const val MODE = "theme__mode"
|
||||
const val DAY_THEME_REF = "theme__day_theme_ref"
|
||||
const val DAY_THEME_ADAPT_TO_APP = "theme__day_theme_adapt_to_app"
|
||||
const val NIGHT_THEME_REF = "theme__night_theme_ref"
|
||||
const val NIGHT_THEME_ADAPT_TO_APP = "theme__night_theme_adapt_to_app"
|
||||
const val SUNRISE_TIME = "theme__sunrise_time"
|
||||
const val SUNSET_TIME = "theme__sunset_time"
|
||||
}
|
||||
|
||||
var colorPrimary: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY, v)
|
||||
var colorPrimaryDark: Int
|
||||
get() = prefHelper.getPref(COLOR_PRIMARY_DARK, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_PRIMARY_DARK, v)
|
||||
var colorAccent: Int
|
||||
get() = prefHelper.getPref(COLOR_ACCENT, 0)
|
||||
set(v) = prefHelper.setPref(COLOR_ACCENT, v)
|
||||
var navBarColor: Int
|
||||
get() = prefHelper.getPref(NAV_BAR_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_COLOR, v)
|
||||
var navBarIsLight: Boolean
|
||||
get() = prefHelper.getPref(NAV_BAR_IS_LIGHT, false)
|
||||
set(v) = prefHelper.setPref(NAV_BAR_IS_LIGHT, v)
|
||||
var keyboardBgColor: Int
|
||||
get() = prefHelper.getPref(KEYBOARD_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEYBOARD_BG_COLOR, v)
|
||||
var keyBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR, v)
|
||||
var keyBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_BG_COLOR_PRESSED, v)
|
||||
var keyFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_FG_COLOR, v)
|
||||
var keyEnterBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR, v)
|
||||
var keyEnterBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_BG_COLOR_PRESSED, v)
|
||||
var keyEnterFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_ENTER_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_ENTER_FG_COLOR, v)
|
||||
var keyShiftBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR, v)
|
||||
var keyShiftBgColorPressed: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_BG_COLOR_PRESSED, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_BG_COLOR_PRESSED, v)
|
||||
var keyShiftFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR, v)
|
||||
var keyShiftFgColorCapsLock: Int
|
||||
get() = prefHelper.getPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, 0)
|
||||
set(v) = prefHelper.setPref(KEY_SHIFT_FG_COLOR_CAPSLOCK, v)
|
||||
var keyPopupBgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR, v)
|
||||
var keyPopupBgColorActive: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_BG_COLOR_ACTIVE, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_BG_COLOR_ACTIVE, v)
|
||||
var keyPopupFgColor: Int
|
||||
get() = prefHelper.getPref(KEY_POPUP_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(KEY_POPUP_FG_COLOR, v)
|
||||
var mediaFgColor: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR, v)
|
||||
var mediaFgColorAlt: Int
|
||||
get() = prefHelper.getPref(MEDIA_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(MEDIA_FG_COLOR_ALT, v)
|
||||
var oneHandedBgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BG_COLOR, v)
|
||||
var oneHandedButtonFgColor: Int
|
||||
get() = prefHelper.getPref(ONE_HANDED_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(ONE_HANDED_BUTTON_FG_COLOR, v)
|
||||
var smartbarBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BG_COLOR, v)
|
||||
var smartbarFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR, v)
|
||||
var smartbarFgColorAlt: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_FG_COLOR_ALT, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_FG_COLOR_ALT, v)
|
||||
var smartbarButtonBgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_BG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_BG_COLOR, v)
|
||||
var smartbarButtonFgColor: Int
|
||||
get() = prefHelper.getPref(SMARTBAR_BUTTON_FG_COLOR, 0)
|
||||
set(v) = prefHelper.setPref(SMARTBAR_BUTTON_FG_COLOR, v)
|
||||
var mode: ThemeMode
|
||||
get() = ThemeMode.fromString(prefHelper.getPref(MODE, ThemeMode.FOLLOW_SYSTEM.toString()))
|
||||
set(v) = prefHelper.setPref(MODE, v)
|
||||
var dayThemeRef: String
|
||||
get() = prefHelper.getPref(DAY_THEME_REF, "assets:ime/theme/floris_day.json")
|
||||
set(v) = prefHelper.setPref(DAY_THEME_REF, v)
|
||||
var dayThemeAdaptToApp: Boolean
|
||||
get() = prefHelper.getPref(DAY_THEME_ADAPT_TO_APP, false)
|
||||
set(v) = prefHelper.setPref(DAY_THEME_ADAPT_TO_APP, v)
|
||||
var nightThemeRef: String
|
||||
get() = prefHelper.getPref(NIGHT_THEME_REF, "assets:ime/theme/floris_night.json")
|
||||
set(v) = prefHelper.setPref(NIGHT_THEME_REF, v)
|
||||
var nightThemeAdaptToApp: Boolean
|
||||
get() = prefHelper.getPref(NIGHT_THEME_ADAPT_TO_APP, false)
|
||||
set(v) = prefHelper.setPref(NIGHT_THEME_ADAPT_TO_APP, v)
|
||||
var sunriseTime: Int
|
||||
get() = prefHelper.getPref(SUNRISE_TIME, TimeUtil.encode(6, 0))
|
||||
set(v) = prefHelper.setPref(SUNRISE_TIME, v)
|
||||
var sunsetTime: Int
|
||||
get() = prefHelper.getPref(SUNSET_TIME, TimeUtil.encode(18, 0))
|
||||
set(v) = prefHelper.setPref(SUNSET_TIME, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,16 +87,10 @@ data class Subtype(
|
||||
* Must be a string which also exists in [FlorisBoard.ImeConfig.characterLayouts]. If the value is
|
||||
* not included within this list, no layout will be shown to the user if the user selects the
|
||||
* predefined layout value.
|
||||
* @property isAsciiCapable Legacy attribute for Android's InputMethodSubtype. Currently no real
|
||||
* use within this project.
|
||||
* @property isEmojiCapable Legacy attribute for Android's InputMethodSubtype. Currently no real
|
||||
* use within this project.
|
||||
*/
|
||||
data class DefaultSubtype(
|
||||
var id: Int,
|
||||
@Json(name = "languageTag")
|
||||
var locale: Locale,
|
||||
var preferredLayout: String,
|
||||
var isAsciiCapable: Boolean,
|
||||
var isEmojiCapable: Boolean
|
||||
var preferredLayout: String
|
||||
)
|
||||
|
||||
@@ -71,7 +71,7 @@ class SubtypeManager(
|
||||
* Loads the [FlorisBoard.ImeConfig] from ime/config.json.
|
||||
*
|
||||
* @param path The path to to IME config file.
|
||||
* @returns The [FlorisBoard.ImeConfig] or a default config.
|
||||
* @return The [FlorisBoard.ImeConfig] or a default config.
|
||||
*/
|
||||
private fun loadImeConfig(path: String): FlorisBoard.ImeConfig {
|
||||
val rawJsonData: String = try {
|
||||
@@ -93,7 +93,7 @@ class SubtypeManager(
|
||||
* Adds a given [subtypeToAdd] to the subtype list, if it does not exist.
|
||||
*
|
||||
* @param subtypeToAdd The subtype which should be added.
|
||||
* @returns True if the subtype was added, false otherwise. A return value of false indicates
|
||||
* @return True if the subtype was added, false otherwise. A return value of false indicates
|
||||
* that the subtype already exists.
|
||||
*/
|
||||
private fun addSubtype(subtypeToAdd: Subtype): Boolean {
|
||||
@@ -112,7 +112,7 @@ class SubtypeManager(
|
||||
*
|
||||
* @param locale The locale of the subtype to be added.
|
||||
* @param layoutName The layout name of the subtype to be added.
|
||||
* @returns True if the subtype was added, false otherwise. A return value of false indicates
|
||||
* @return True if the subtype was added, false otherwise. A return value of false indicates
|
||||
* that the subtype already exists.
|
||||
*/
|
||||
fun addSubtype(locale: Locale, layoutName: String): Boolean {
|
||||
@@ -129,7 +129,7 @@ class SubtypeManager(
|
||||
* Gets the active subtype and returns it. If the activeSubtypeId points to a non-existent
|
||||
* subtype, this method tries to determine a new active subtype.
|
||||
*
|
||||
* @returns The active subtype or null, if the subtype list is empty or no new active subtype
|
||||
* @return The active subtype or null, if the subtype list is empty or no new active subtype
|
||||
* could be determined.
|
||||
*/
|
||||
fun getActiveSubtype(): Subtype? {
|
||||
@@ -152,7 +152,7 @@ class SubtypeManager(
|
||||
* Gets a subtype by the given [id].
|
||||
*
|
||||
* @param id The id of the subtype you want to get.
|
||||
* @returns The subtype or null, if no matching subtype could be found.
|
||||
* @return The subtype or null, if no matching subtype could be found.
|
||||
*/
|
||||
fun getSubtypeById(id: Int): Subtype? {
|
||||
for (subtype in subtypes) {
|
||||
@@ -167,7 +167,7 @@ class SubtypeManager(
|
||||
* Gets the default system subtype for a given [locale].
|
||||
*
|
||||
* @param locale The locale of the default system subtype to get.
|
||||
* @returns The default system locale or null, if no matching default system subtype could be
|
||||
* @return The default system locale or null, if no matching default system subtype could be
|
||||
* found.
|
||||
*/
|
||||
fun getDefaultSubtypeForLocale(locale: Locale): DefaultSubtype? {
|
||||
@@ -217,10 +217,38 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the previous subtype in the subtype list if possible.
|
||||
*
|
||||
* @return The new active subtype or null if the determination process failed.
|
||||
*/
|
||||
fun switchToPrevSubtype(): Subtype? {
|
||||
val subtypeList = subtypes
|
||||
val activeSubtype = getActiveSubtype() ?: return null
|
||||
var triggerNextSubtype = false
|
||||
var newActiveSubtype: Subtype? = null
|
||||
for (subtype in subtypeList.reversed()) {
|
||||
if (triggerNextSubtype) {
|
||||
triggerNextSubtype = false
|
||||
newActiveSubtype = subtype
|
||||
} else if (subtype == activeSubtype) {
|
||||
triggerNextSubtype = true
|
||||
}
|
||||
}
|
||||
if (triggerNextSubtype) {
|
||||
newActiveSubtype = subtypeList.last()
|
||||
}
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> Subtype.DEFAULT.id
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
return newActiveSubtype
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the next subtype in the subtype list if possible.
|
||||
*
|
||||
* @returns The new active subtype or null if the determination process failed.
|
||||
* @return The new active subtype or null if the determination process failed.
|
||||
*/
|
||||
fun switchToNextSubtype(): Subtype? {
|
||||
val subtypeList = subtypes
|
||||
@@ -236,10 +264,10 @@ class SubtypeManager(
|
||||
}
|
||||
}
|
||||
if (triggerNextSubtype) {
|
||||
newActiveSubtype = subtypeList[0]
|
||||
newActiveSubtype = subtypeList.first()
|
||||
}
|
||||
prefs.localization.activeSubtypeId = when (newActiveSubtype) {
|
||||
null -> -1
|
||||
null -> Subtype.DEFAULT.id
|
||||
else -> newActiveSubtype.id
|
||||
}
|
||||
return newActiveSubtype
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.extension
|
||||
|
||||
import android.content.Context
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Result
|
||||
|
||||
/**
|
||||
* Interface for an Asset to use within FlorisBoard. An asset is everything from a dictionary to a
|
||||
* keyboard layout to a extended popup mapping, etc. Assets are very important for the splitting
|
||||
* FlorisBoard's resources into assets.
|
||||
*
|
||||
* NOTE: At the current state, this is only a simple implementation idea and only PopupMappingAsset
|
||||
* partly uses it. This package and it's classes are expected to grow and gain more importance over
|
||||
* time.
|
||||
*/
|
||||
interface Asset {
|
||||
/**
|
||||
* The name of the Asset, must be unique throughout all Assets. Is used to internally identify
|
||||
* and sort the Asset. This name is non-translatable and thus is a static string.
|
||||
*/
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* The display name of the Asset. This is the label which will be shown to the user in the
|
||||
* Settings UI. Currently also a static string.
|
||||
* TODO: make this string localize-able
|
||||
*/
|
||||
val label: String
|
||||
|
||||
/**
|
||||
* A list of authors who actively worked on the content of this Asset. Any content of string is
|
||||
* valid, but the best practice is to use the GitHub username.
|
||||
*/
|
||||
val authors: List<String>
|
||||
|
||||
/**
|
||||
* "Static" functions which every Asset should provide.
|
||||
*/
|
||||
interface Companion<T> {
|
||||
/**
|
||||
* Creates an empty Asset of type [T].
|
||||
*/
|
||||
fun empty(): T
|
||||
|
||||
/**
|
||||
* Loads an Asset of type [T] from the specified path.
|
||||
*/
|
||||
fun fromFile(context: Context, path: String): Result<T, Throwable> = Err(NotImplementedError())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.extension
|
||||
|
||||
import android.content.Context
|
||||
import com.github.michaelbull.result.*
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.patrickgold.florisboard.ime.popup.PopupExtension
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyTypeAdapter
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariationAdapter
|
||||
import dev.patrickgold.florisboard.ime.text.layout.LayoutTypeAdapter
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
class AssetManager private constructor(private val applicationContext: Context) {
|
||||
private val moshi: Moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
/*.add(PolymorphicJsonAdapterFactory.of(Asset::class.java, "\$type")
|
||||
.withSubtype(PopupExtension::class.java, PopupExtension::class.qualifiedName)
|
||||
.withSubtype(Theme::class.java, Theme::class.qualifiedName)
|
||||
)*/
|
||||
.add(LayoutTypeAdapter())
|
||||
.add(KeyTypeAdapter())
|
||||
.add(KeyVariationAdapter())
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
private var defaultInstance: AssetManager? = null
|
||||
|
||||
fun init(applicationContext: Context): AssetManager {
|
||||
val instance = AssetManager(applicationContext)
|
||||
defaultInstance = instance
|
||||
return instance
|
||||
}
|
||||
|
||||
fun default(): AssetManager {
|
||||
val instance = defaultInstance
|
||||
if (instance != null) {
|
||||
return instance
|
||||
} else {
|
||||
throw UninitializedPropertyAccessException(
|
||||
"${this::class.simpleName} has not been initialized previously. Make sure to call init(applicationContext) before using default()."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteAsset(ref: AssetRef): Result<Nothing?, Throwable> {
|
||||
return when (ref.source) {
|
||||
AssetSource.Internal -> {
|
||||
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
|
||||
if (file.isFile) {
|
||||
val success = file.delete()
|
||||
if (success) {
|
||||
Ok(null)
|
||||
} else {
|
||||
Err(Exception("Could not delete file."))
|
||||
}
|
||||
} else {
|
||||
Err(Exception("Provided reference is not a file."))
|
||||
}
|
||||
}
|
||||
else -> Err(Exception("Can not delete an asset in source '${ref.source}'"))
|
||||
}
|
||||
}
|
||||
|
||||
fun hasAsset(ref: AssetRef): Boolean {
|
||||
return when (ref.source) {
|
||||
AssetSource.Assets -> {
|
||||
try {
|
||||
val file = File(ref.path)
|
||||
val list = applicationContext.assets.list(file.parent?.toString() ?: "")
|
||||
list?.contains(file.name) == true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
AssetSource.Internal -> {
|
||||
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
|
||||
file.exists() && file.isFile
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Asset> listAssets(ref: AssetRef, assetClass: Class<T>): Result<Map<AssetRef, T>, Throwable> {
|
||||
val retMap = mutableMapOf<AssetRef, T>()
|
||||
return when (ref.source) {
|
||||
AssetSource.Assets -> {
|
||||
try {
|
||||
val list = applicationContext.assets.list(ref.path)
|
||||
if (list != null) {
|
||||
for (file in list) {
|
||||
val fileRef = ref.copy(path = ref.path + "/" + file)
|
||||
val assetResult = loadAsset(fileRef, assetClass)
|
||||
assetResult.onSuccess { asset ->
|
||||
retMap[fileRef.copy()] = asset
|
||||
}.onFailure { error ->
|
||||
Timber.e(error.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(retMap.toMap())
|
||||
} catch (e: Exception) {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
AssetSource.Internal -> {
|
||||
val dir = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
|
||||
if (dir.isDirectory) {
|
||||
dir.listFiles()?.let {
|
||||
it.forEach { file ->
|
||||
if (file.isFile) {
|
||||
val fileRef = ref.copy(path = ref.path + "/" + file.name)
|
||||
val assetResult = loadAsset(fileRef, assetClass)
|
||||
assetResult.onSuccess { asset ->
|
||||
retMap[fileRef.copy()] = asset
|
||||
}.onFailure { error ->
|
||||
Timber.e(error.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(retMap.toMap())
|
||||
}
|
||||
else -> Ok(retMap.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Asset> loadAsset(ref: AssetRef, assetClass: Class<T>): Result<T, Throwable> {
|
||||
val rawJsonData = when (ref.source) {
|
||||
is AssetSource.Assets -> {
|
||||
try {
|
||||
applicationContext.assets.open(ref.path).bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
is AssetSource.Internal -> {
|
||||
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
|
||||
val contents = readFile(file)
|
||||
if (contents.isBlank()) {
|
||||
"{}"
|
||||
} else {
|
||||
contents
|
||||
}
|
||||
}
|
||||
else -> "{}"
|
||||
}
|
||||
return try {
|
||||
val adapter = moshi.adapter(assetClass)
|
||||
val asset = adapter.fromJson(rawJsonData)
|
||||
if (asset != null) {
|
||||
Ok(asset)
|
||||
} else {
|
||||
Err(NullPointerException("Asset failed to load!"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Asset> writeAsset(ref: AssetRef, assetClass: Class<T>, asset: T): Result<Boolean, Throwable> {
|
||||
return when (ref.source) {
|
||||
AssetSource.Internal -> {
|
||||
val adapter = moshi.adapter(assetClass)
|
||||
val rawJson = adapter.toJson(asset)
|
||||
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
|
||||
writeToFile(file, rawJson)
|
||||
Ok(true)
|
||||
}
|
||||
else -> Err(Exception("Can not write an asset in source '${ref.source}'"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a given [file] and returns its content.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun readFile(file: File): String {
|
||||
val retText = StringBuilder()
|
||||
if (file.exists()) {
|
||||
val newLine = System.lineSeparator()
|
||||
file.forEachLine {
|
||||
retText.append(it)
|
||||
retText.append(newLine)
|
||||
}
|
||||
}
|
||||
return retText.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes given [text] to given [file]. If the file already exists, its current content
|
||||
* will be overwritten.
|
||||
*
|
||||
* @param file The file object.
|
||||
* @param text The text to write to the file.
|
||||
* @return The contents of the file or an empty string, if the file does not exist.
|
||||
*/
|
||||
private fun writeToFile(file: File, text: String) {
|
||||
try {
|
||||
file.parent?.let {
|
||||
val dir = File(it)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
}
|
||||
file.writeText(text)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.extension
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
|
||||
/**
|
||||
* Data class which is a reference to an asset file. It indicates in which storage medium the asset
|
||||
* is as well as the relative path to it.
|
||||
*
|
||||
* @property source The source in which the asset is (APK assets, internal storage, external)
|
||||
* @property path The relative path to the asset within [source]. Must not begin and end with a
|
||||
* forward slash.
|
||||
*/
|
||||
data class AssetRef(
|
||||
val source: AssetSource,
|
||||
val path: String
|
||||
) {
|
||||
companion object {
|
||||
private const val DELIMITER: String = ":"
|
||||
|
||||
fun fromString(str: String): Result<AssetRef, String> {
|
||||
val items = str.split(DELIMITER)
|
||||
if (items.size != 2) {
|
||||
return Err("Unexpected length of given asset ref. Make sure that the asset ref string contains exactly 2 items separated by '$DELIMITER'!")
|
||||
}
|
||||
val retSource = AssetSource.fromString(items[0]).getOrElse {
|
||||
return Err(it)
|
||||
}
|
||||
return Ok(AssetRef(retSource, items[1]))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val retString: StringBuilder = StringBuilder().apply {
|
||||
append(source.toString())
|
||||
append(DELIMITER)
|
||||
append(path)
|
||||
}
|
||||
return retString.toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.extension
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Sealed class which specifies where an asset comes from. There are 3 different types, all of which
|
||||
* require a different approach on how to access the actual asset.
|
||||
*/
|
||||
sealed class AssetSource {
|
||||
/**
|
||||
* The asset comes pre-built with the application, thus all paths must be relative to the asset
|
||||
* directory of FlorisBoard.
|
||||
*/
|
||||
object Assets : AssetSource()
|
||||
|
||||
/**
|
||||
* The asset is saved in the internal storage of FlorisBoard, all relative paths must therefore
|
||||
* be treated as such.
|
||||
*/
|
||||
object Internal : AssetSource()
|
||||
|
||||
/**
|
||||
* Asset source is an external extension, which requires the package name and possibly other
|
||||
* data. Currently NYI.
|
||||
* TODO: Implement external extensions
|
||||
*/
|
||||
data class External(val packageName: String) : AssetSource() {
|
||||
override fun toString(): String {
|
||||
return super.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val externalRegex: Regex = """^external\\(([a-z]+\\.)*[a-z]+\\)\$""".toRegex()
|
||||
|
||||
fun fromString(str: String): Result<AssetSource, String> {
|
||||
return when (val string = str.toLowerCase(Locale.ENGLISH)) {
|
||||
"assets" -> Ok(Assets)
|
||||
"internal" -> Ok(Internal)
|
||||
else -> {
|
||||
if (string.matches(externalRegex)) {
|
||||
val packageName = string.substring(9, string.length - 1)
|
||||
Ok(External(packageName))
|
||||
} else {
|
||||
Err("'$str' is not a valid AssetSource.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return when (this) {
|
||||
is Assets -> "assets"
|
||||
is Internal -> "internal"
|
||||
is External -> "external($packageName)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,13 @@
|
||||
package dev.patrickgold.florisboard.ime.media
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.os.Handler
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.EditorInstance
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.InputView
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiKeyData
|
||||
@@ -33,7 +33,10 @@ import dev.patrickgold.florisboard.ime.media.emoticon.EmoticonKeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyType
|
||||
import dev.patrickgold.florisboard.util.cancelAll
|
||||
import dev.patrickgold.florisboard.util.postAtScheduledRate
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -50,10 +53,12 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
FlorisBoard.EventListener {
|
||||
|
||||
private val florisboard = FlorisBoard.getInstance()
|
||||
private val activeEditorInstance: EditorInstance
|
||||
get() = florisboard.activeEditorInstance
|
||||
|
||||
private var activeTab: Tab? = null
|
||||
private var mediaViewFlipper: ViewFlipper? = null
|
||||
private var osTimer: Timer? = null
|
||||
private var repeatedKeyPressHandler: Handler? = null
|
||||
private var tabLayout: TabLayout? = null
|
||||
private val tabViews = EnumMap<Tab, LinearLayout>(Tab::class.java)
|
||||
|
||||
@@ -75,6 +80,11 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
florisboard.addEventListener(this)
|
||||
}
|
||||
|
||||
override fun onCreateInputView() {
|
||||
super.onCreateInputView()
|
||||
repeatedKeyPressHandler = Handler(florisboard.context.mainLooper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new input view has been registered. Used to initialize all media-relevant
|
||||
* views and layouts.
|
||||
@@ -82,7 +92,7 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onRegisterInputView(inputView: InputView) {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onRegisterInputView(inputView)")
|
||||
Timber.i("onRegisterInputView(inputView)")
|
||||
|
||||
launch(Dispatchers.Default) {
|
||||
mediaViewGroup = inputView.findViewById(R.id.media_input)
|
||||
@@ -108,15 +118,12 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {}
|
||||
})
|
||||
|
||||
for (tab in Tab.values()) {
|
||||
val tabView = createTabViewFor(tab)
|
||||
tabViews[tab] = tabView
|
||||
withContext(Dispatchers.Main) {
|
||||
withContext(Dispatchers.Main) {
|
||||
for (tab in Tab.values()) {
|
||||
val tabView = createTabViewFor(tab)
|
||||
tabViews[tab] = tabView
|
||||
mediaViewFlipper?.addView(tabView)
|
||||
}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
tabLayout?.selectTab(tabLayout?.getTabAt(0))
|
||||
}
|
||||
}
|
||||
@@ -126,7 +133,7 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
* Clean-up of resources and stopping all coroutines.
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
if (BuildConfig.DEBUG) Log.i(this::class.simpleName, "onDestroy()")
|
||||
Timber.i("onDestroy()")
|
||||
|
||||
cancel()
|
||||
instance = null
|
||||
@@ -139,10 +146,10 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
event ?: return false
|
||||
val data = when (view.id) {
|
||||
R.id.media_input_switch_to_text_input_button -> {
|
||||
KeyData(KeyCode.SWITCH_TO_TEXT_CONTEXT)
|
||||
KeyData(code = KeyCode.SWITCH_TO_TEXT_CONTEXT)
|
||||
}
|
||||
R.id.media_input_backspace_button -> {
|
||||
KeyData(KeyCode.DELETE, type = KeyType.ENTER_EDITING)
|
||||
KeyData(code = KeyCode.DELETE, type = KeyType.ENTER_EDITING)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@@ -151,17 +158,14 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound(data)
|
||||
if (data?.code == KeyCode.DELETE && data.type == KeyType.ENTER_EDITING) {
|
||||
osTimer = Timer()
|
||||
osTimer?.scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
florisboard.textInputManager.sendKeyPress(data)
|
||||
}
|
||||
}, 500, 50)
|
||||
val delayMillis = florisboard.prefs.keyboard.longPressDelay.toLong()
|
||||
repeatedKeyPressHandler?.postAtScheduledRate(delayMillis, 25) {
|
||||
florisboard.textInputManager.sendKeyPress(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
osTimer?.cancel()
|
||||
osTimer = null
|
||||
repeatedKeyPressHandler?.cancelAll()
|
||||
if (event.actionMasked != MotionEvent.ACTION_CANCEL && data != null) {
|
||||
florisboard.textInputManager.sendKeyPress(data)
|
||||
}
|
||||
@@ -199,18 +203,14 @@ class MediaInputManager private constructor() : CoroutineScope by MainScope(),
|
||||
* Sends a given [emojiKeyData] to the current input editor.
|
||||
*/
|
||||
fun sendEmojiKeyPress(emojiKeyData: EmojiKeyData) {
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.finishComposingText()
|
||||
ic?.commitText(emojiKeyData.getCodePointsAsString(), 1)
|
||||
activeEditorInstance.commitText(emojiKeyData.getCodePointsAsString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given [emoticonKeyData] to the current input editor.
|
||||
*/
|
||||
fun sendEmoticonKeyPress(emoticonKeyData: EmoticonKeyData) {
|
||||
val ic = florisboard.currentInputConnection
|
||||
ic?.finishComposingText()
|
||||
ic?.commitText(emoticonKeyData.icon, 1)
|
||||
activeEditorInstance.commitText(emoticonKeyData.icon)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,11 +25,14 @@ import android.widget.LinearLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MediaInputView : LinearLayout, FlorisBoard.EventListener {
|
||||
class MediaInputView : LinearLayout, FlorisBoard.EventListener,
|
||||
ThemeManager.OnThemeUpdatedListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private val themeManager: ThemeManager = ThemeManager.default()
|
||||
|
||||
var tabLayout: TabLayout? = null
|
||||
private set
|
||||
@@ -46,22 +49,30 @@ class MediaInputView : LinearLayout, FlorisBoard.EventListener {
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
themeManager.registerOnThemeUpdatedListener(this)
|
||||
tabLayout = findViewById(R.id.media_input_tabs)
|
||||
switchToTextInputButton = findViewById(R.id.media_input_switch_to_text_input_button)
|
||||
backspaceButton = findViewById(R.id.media_input_backspace_button)
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout?.setTabTextColors(prefs.theme.mediaFgColor, prefs.theme.mediaFgColor)
|
||||
tabLayout?.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout?.setSelectedTabIndicatorColor(prefs.theme.colorPrimary)
|
||||
switchToTextInputButton?.setTextColor(prefs.theme.mediaFgColor)
|
||||
backspaceButton?.imageTintList = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
themeManager.unregisterOnThemeUpdatedListener(this)
|
||||
}
|
||||
|
||||
override fun onThemeUpdated(theme: Theme) {
|
||||
val fgColor = theme.getAttr(Theme.Attr.MEDIA_FOREGROUND).toSolidColor().color
|
||||
val colorPrimary = theme.getAttr(Theme.Attr.WINDOW_COLOR_PRIMARY).toSolidColor().color
|
||||
tabLayout?.setTabTextColors(fgColor, fgColor)
|
||||
tabLayout?.tabIconTint = ColorStateList.valueOf(fgColor)
|
||||
tabLayout?.setSelectedTabIndicatorColor(colorPrimary)
|
||||
switchToTextInputButton?.setTextColor(fgColor)
|
||||
backspaceButton?.imageTintList = ColorStateList.valueOf(fgColor)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = florisboard?.inputView?.desiredInputViewHeight ?: 0
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
|
||||
val height = florisboard?.inputView?.desiredMediaKeyboardViewHeight ?: 0.0f
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height.roundToInt(), MeasureSpec.EXACTLY))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,21 @@ package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.ScrollView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
|
||||
/**
|
||||
* View class for managing the rendering and the events of a single emoji keyboard key.
|
||||
@@ -46,7 +47,7 @@ class EmojiKeyView(
|
||||
private val emojiKeyboardView: EmojiKeyboardView,
|
||||
val data: EmojiKeyData
|
||||
) : androidx.appcompat.widget.AppCompatTextView(emojiKeyboardView.context),
|
||||
FlorisBoard.EventListener {
|
||||
FlorisBoard.EventListener, ThemeManager.OnThemeUpdatedListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
@@ -91,11 +92,11 @@ class EmojiKeyView(
|
||||
osHandler = Handler()
|
||||
}
|
||||
osHandler?.postDelayed({
|
||||
(parent.parent as HorizontalScrollView)
|
||||
(parent.parent as ScrollView)
|
||||
.requestDisallowInterceptTouchEvent(true)
|
||||
emojiKeyboardView.isScrollBlocked = true
|
||||
emojiKeyboardView.popupManager.show(this)
|
||||
emojiKeyboardView.popupManager.extend(this)
|
||||
emojiKeyboardView.popupManager.show(this, KeyHintMode.DISABLED)
|
||||
emojiKeyboardView.popupManager.extend(this, KeyHintMode.DISABLED)
|
||||
florisboard?.keyPressVibrate()
|
||||
florisboard?.keyPressSound()
|
||||
}, delayMillis.toLong())
|
||||
@@ -148,10 +149,10 @@ class EmojiKeyView(
|
||||
)
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
override fun onThemeUpdated(theme: Theme) {
|
||||
triangleDrawable?.colorFilter =
|
||||
BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
prefs.theme.mediaFgColorAlt, BlendModeCompat.SRC_ATOP
|
||||
theme.getAttr(Theme.Attr.MEDIA_FOREGROUND_ALT).toSolidColor().color, BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,19 +21,19 @@ import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ViewFlipper
|
||||
import android.widget.*
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.FlorisBoard
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.ime.popup.KeyPopupManager
|
||||
import dev.patrickgold.florisboard.ime.popup.PopupManager
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
|
||||
@@ -44,9 +44,10 @@ import java.util.*
|
||||
*
|
||||
* @property florisboard Reference to instance of core class [FlorisBoard].
|
||||
*/
|
||||
class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener,
|
||||
ThemeManager.OnThemeUpdatedListener {
|
||||
private val florisboard: FlorisBoard? = FlorisBoard.getInstanceOrNull()
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private val themeManager: ThemeManager = ThemeManager.default()
|
||||
|
||||
private var activeCategory: EmojiCategory = EmojiCategory.SMILEYS_EMOTION
|
||||
private var emojiViewFlipper: ViewFlipper
|
||||
@@ -55,10 +56,10 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
private var layouts: Deferred<EmojiLayoutDataMap>
|
||||
private val mainScope = MainScope()
|
||||
private val tabLayout: TabLayout
|
||||
private val uiLayouts = EnumMap<EmojiCategory, HorizontalScrollView>(EmojiCategory::class.java)
|
||||
private val uiLayouts = EnumMap<EmojiCategory, ScrollView>(EmojiCategory::class.java)
|
||||
|
||||
var isScrollBlocked: Boolean = false
|
||||
var popupManager = KeyPopupManager<EmojiKeyboardView, EmojiKeyView>(this)
|
||||
var popupManager = PopupManager<EmojiKeyboardView, EmojiKeyView>(this, florisboard?.popupLayerView)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
@@ -66,12 +67,15 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
layouts = mainScope.async(Dispatchers.IO) {
|
||||
parseRawEmojiSpecsFile(context, "ime/media/emoji/emoji-test.txt")
|
||||
}
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
orientation = VERTICAL
|
||||
|
||||
emojiViewFlipper = ViewFlipper(context)
|
||||
emojiViewFlipper.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
emojiViewFlipper.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 0).apply {
|
||||
weight = 1.0f
|
||||
}
|
||||
emojiViewFlipper.measureAllChildren = false
|
||||
addView(emojiViewFlipper)
|
||||
|
||||
@@ -102,12 +106,18 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
themeManager.registerOnThemeUpdatedListener(this)
|
||||
mainScope.launch {
|
||||
layouts.await()
|
||||
buildLayout()
|
||||
setActiveCategory(EmojiCategory.SMILEYS_EMOTION)
|
||||
themeManager.requestThemeUpdate(this@EmojiKeyboardView)
|
||||
}
|
||||
onApplyThemeAttributes()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
themeManager.unregisterOnThemeUpdatedListener(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,10 +127,10 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
*/
|
||||
private suspend fun buildLayout() = withContext(Dispatchers.Default) {
|
||||
for (category in EmojiCategory.values()) {
|
||||
val hsv = buildLayoutForCategory(category)
|
||||
uiLayouts[category] = hsv
|
||||
val scrollView = buildLayoutForCategory(category)
|
||||
uiLayouts[category] = scrollView
|
||||
withContext(Dispatchers.Main) {
|
||||
emojiViewFlipper.addView(hsv)
|
||||
emojiViewFlipper.addView(scrollView)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,18 +140,19 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
* context and will not block the main UI thread.
|
||||
*
|
||||
* @param category The category for which a layout should be built.
|
||||
* @return The layout (top-most view is a [HorizontalScrollView]).
|
||||
* @return The layout (top-most view is a [ScrollView]).
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private suspend fun buildLayoutForCategory(
|
||||
category: EmojiCategory
|
||||
): HorizontalScrollView = withContext(Dispatchers.Default) {
|
||||
val hsv = HorizontalScrollView(context)
|
||||
hsv.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
||||
): ScrollView = withContext(Dispatchers.Default) {
|
||||
val scrollView = ScrollView(context)
|
||||
scrollView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
val flexboxLayout = FlexboxLayout(context)
|
||||
flexboxLayout.layoutParams =
|
||||
LayoutParams(LayoutParams.WRAP_CONTENT, emojiKeyHeight * 3)
|
||||
flexboxLayout.flexDirection = FlexDirection.COLUMN
|
||||
LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
flexboxLayout.flexDirection = FlexDirection.ROW
|
||||
flexboxLayout.justifyContent = JustifyContent.SPACE_BETWEEN
|
||||
flexboxLayout.flexWrap = FlexWrap.WRAP
|
||||
for (emojiKeyData in layouts.await()[category].orEmpty()) {
|
||||
val emojiKeyView =
|
||||
@@ -151,11 +162,30 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
)
|
||||
flexboxLayout.addView(emojiKeyView)
|
||||
}
|
||||
hsv.setOnTouchListener { _, _ ->
|
||||
// Add empty placeholder emojis at the end so the grid view. Below is an illustration how
|
||||
// the UI looks with and without an placeholder (e = emoji):
|
||||
// Without placeholder With placeholder
|
||||
// e e e e e e e e e e e e e e
|
||||
// ............. .............
|
||||
// e e e e e e e e e e e e e e
|
||||
// e e e e e e e e
|
||||
//
|
||||
// Based on this SO's answer idea (by La Nube - Luis R. Díaz Muñiz):
|
||||
// https://stackoverflow.com/a/31478004/6801193
|
||||
//
|
||||
// 24 items are chosen here because that's probably the max items that will be shown per
|
||||
// row, even in landscape mode.
|
||||
for (n in 0 until 24) {
|
||||
val gridPlaceholderView = View(context).apply {
|
||||
layoutParams = LayoutParams(emojiKeyWidth, 0)
|
||||
}
|
||||
flexboxLayout.addView(gridPlaceholderView)
|
||||
}
|
||||
scrollView.setOnTouchListener { _, _ ->
|
||||
return@setOnTouchListener isScrollBlocked
|
||||
}
|
||||
hsv.addView(flexboxLayout)
|
||||
return@withContext hsv
|
||||
scrollView.addView(flexboxLayout)
|
||||
return@withContext scrollView
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,8 +224,10 @@ class EmojiKeyboardView : LinearLayout, FlorisBoard.EventListener {
|
||||
isScrollBlocked = true
|
||||
}
|
||||
|
||||
override fun onApplyThemeAttributes() {
|
||||
tabLayout.tabIconTint = ColorStateList.valueOf(prefs.theme.mediaFgColor)
|
||||
tabLayout.setSelectedTabIndicatorColor(prefs.theme.colorAccent)
|
||||
override fun onThemeUpdated(theme: Theme) {
|
||||
val fgColor = theme.getAttr(Theme.Attr.MEDIA_FOREGROUND).toSolidColor().color
|
||||
val colorAccent = theme.getAttr(Theme.Attr.WINDOW_COLOR_ACCENT).toSolidColor().color
|
||||
tabLayout.tabIconTint = ColorStateList.valueOf(fgColor)
|
||||
tabLayout.setSelectedTabIndicatorColor(colorAccent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.util.Log
|
||||
import androidx.core.graphics.PaintCompat
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
@@ -171,13 +172,13 @@ fun parseRawEmojiSpecsFile(
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e("EmojiLayoutDataMap", "parseRawEmojiSpecsFile(): $e")
|
||||
Timber.e("parseRawEmojiSpecsFile(): $e")
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close()
|
||||
} catch (e: IOException) {
|
||||
Log.e("EmojiLayoutDataMap", "parseRawEmojiSpecsFile(): $e")
|
||||
Timber.e("parseRawEmojiSpecsFile(): $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class EmoticonKeyView : androidx.appcompat.widget.AppCompatTextView {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
setBackgroundColor(getColorFromAttr(context, R.attr.semiTransparentColor))
|
||||
florisboard.keyPressVibrate()
|
||||
florisboard.keyPressSound(KeyData(0))
|
||||
florisboard.keyPressSound(KeyData())
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
setBackgroundColor(Color.TRANSPARENT)
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class KeyPopupExtendedSingleView(
|
||||
context: Context, var isActive: Boolean = false
|
||||
) : androidx.appcompat.widget.AppCompatTextView(
|
||||
context, null, 0
|
||||
) {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
var iconDrawable: Drawable? = null
|
||||
|
||||
init {
|
||||
background = getDrawable(context, R.drawable.shape_rect_rounded)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, when {
|
||||
isActive -> prefs.theme.keyPopupBgColorActive
|
||||
else -> Color.TRANSPARENT
|
||||
})
|
||||
setTextColor(prefs.theme.keyPopupFgColor)
|
||||
|
||||
super.onDraw(canvas)
|
||||
|
||||
canvas ?: return
|
||||
|
||||
val drawable = iconDrawable
|
||||
val drawablePadding = (0.2f * measuredHeight).toInt()
|
||||
if (drawable != null) {
|
||||
var marginV = 0
|
||||
var marginH = 0
|
||||
if (measuredWidth > measuredHeight) {
|
||||
marginH = (measuredWidth - measuredHeight) / 2
|
||||
} else {
|
||||
marginV = (measuredHeight - measuredWidth) / 2
|
||||
}
|
||||
drawable.setBounds(
|
||||
marginH + drawablePadding,
|
||||
marginV + drawablePadding,
|
||||
measuredWidth - marginH - drawablePadding,
|
||||
measuredHeight - marginV - drawablePadding)
|
||||
drawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
|
||||
prefs.theme.keyPopupFgColor,
|
||||
BlendModeCompat.SRC_ATOP
|
||||
)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupView : LinearLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
private lateinit var text: TextView
|
||||
private lateinit var threedots: ImageView
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
text = findViewById(R.id.key_popup_text)
|
||||
threedots = findViewById(R.id.key_popup_threedots)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
text.setTextColor(prefs.theme.keyPopupFgColor)
|
||||
setImageTintColor2(threedots, prefs.theme.keyPopupFgColor)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.PaintDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.theme.Theme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.util.ViewLayoutUtils
|
||||
import kotlin.math.min
|
||||
|
||||
class PopupExtendedView : View, ThemeManager.OnThemeUpdatedListener {
|
||||
private val themeManager: ThemeManager = ThemeManager.default()
|
||||
|
||||
private val activeBackgroundDrawable: PaintDrawable = PaintDrawable()
|
||||
private var backgroundDrawable: PaintDrawable = PaintDrawable()
|
||||
private val labelPaint: Paint = Paint().apply {
|
||||
alpha = 255
|
||||
color = 0
|
||||
isAntiAlias = true
|
||||
isFakeBoldText = false
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
typeface = Typeface.DEFAULT
|
||||
}
|
||||
private val tldPaint: Paint = Paint().apply {
|
||||
alpha = 255
|
||||
color = 0
|
||||
isAntiAlias = true
|
||||
isFakeBoldText = false
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = resources.getDimension(R.dimen.key_textSize)
|
||||
typeface = Typeface.DEFAULT
|
||||
}
|
||||
|
||||
val properties: Properties = Properties(
|
||||
width = resources.getDimension(R.dimen.key_width).toInt(),
|
||||
height = resources.getDimension(R.dimen.key_height).toInt(),
|
||||
xOffset = 0,
|
||||
yOffset = 0,
|
||||
gravity = Gravity.START,
|
||||
elements = mutableListOf(),
|
||||
activeElementIndex = -1,
|
||||
labelTextSize = resources.getDimension(R.dimen.key_popup_textSize),
|
||||
)
|
||||
val isShowing: Boolean
|
||||
get() = visibility == VISIBLE
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
init {
|
||||
visibility = GONE
|
||||
background = backgroundDrawable
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
themeManager.registerOnThemeUpdatedListener(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
themeManager.unregisterOnThemeUpdatedListener(this)
|
||||
}
|
||||
|
||||
override fun onThemeUpdated(theme: Theme) {
|
||||
activeBackgroundDrawable.apply {
|
||||
setTint(theme.getAttr(Theme.Attr.POPUP_BACKGROUND_ACTIVE).toSolidColor().color)
|
||||
setCornerRadius(ViewLayoutUtils.convertDpToPixel(6.0f, context))
|
||||
}
|
||||
backgroundDrawable.apply {
|
||||
setTint(theme.getAttr(Theme.Attr.POPUP_BACKGROUND).toSolidColor().color)
|
||||
setCornerRadius(ViewLayoutUtils.convertDpToPixel(6.0f, context))
|
||||
}
|
||||
elevation = ViewLayoutUtils.convertDpToPixel(4.0f, context)
|
||||
labelPaint.color = theme.getAttr(Theme.Attr.POPUP_FOREGROUND).toSolidColor().color
|
||||
tldPaint.color = theme.getAttr(Theme.Attr.POPUP_FOREGROUND).toSolidColor().color
|
||||
if (isShowing) {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyProperties(anchor: View) {
|
||||
val anchorCoords = IntArray(2)
|
||||
anchor.getLocationInWindow(anchorCoords)
|
||||
val anchorX = anchorCoords[0]
|
||||
val anchorY = anchorCoords[1] + anchor.measuredHeight
|
||||
when (val lp = layoutParams) {
|
||||
is FrameLayout.LayoutParams -> lp.apply {
|
||||
width = properties.width
|
||||
height = properties.height
|
||||
setMargins(
|
||||
anchorX + properties.xOffset,
|
||||
anchorY + properties.yOffset,
|
||||
0,
|
||||
0
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
layoutParams = FrameLayout.LayoutParams(properties.width, properties.height).apply {
|
||||
setMargins(
|
||||
anchorX + properties.xOffset,
|
||||
anchorY + properties.yOffset,
|
||||
0,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
labelPaint.textSize = properties.labelTextSize
|
||||
tldPaint.textSize = properties.labelTextSize * 0.6f
|
||||
if (isShowing) {
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(anchor: View) {
|
||||
applyProperties(anchor)
|
||||
visibility = VISIBLE
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
visibility = GONE
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
canvas ?: return
|
||||
|
||||
if (properties.elements.isEmpty() || properties.elements.first().isEmpty()) {
|
||||
return
|
||||
}
|
||||
val baseSize = properties.elements.first().size
|
||||
val elementWidth = measuredWidth / baseSize
|
||||
val elementHeight = measuredHeight / properties.elements.size
|
||||
|
||||
var currentElementIndex = 0
|
||||
for ((r, row) in properties.elements.reversed().withIndex()) {
|
||||
val leftOffset = when (properties.gravity) {
|
||||
Gravity.END -> baseSize - row.size
|
||||
else -> 0
|
||||
}
|
||||
for ((e, element) in row.withIndex()) {
|
||||
val left = (e + leftOffset) * elementWidth
|
||||
val top = r * elementHeight
|
||||
if (properties.activeElementIndex == currentElementIndex) {
|
||||
activeBackgroundDrawable.setBounds(
|
||||
left, top, left + elementWidth, top + elementHeight
|
||||
)
|
||||
activeBackgroundDrawable.draw(canvas)
|
||||
}
|
||||
when (element) {
|
||||
is Element.Label -> {
|
||||
val label = element.label
|
||||
if (label.isNotEmpty()) {
|
||||
val centerX = left + elementWidth / 2.0f
|
||||
val centerY = top + elementHeight / 2.0f + (labelPaint.textSize - labelPaint.descent()) / 2
|
||||
canvas.drawText(label, centerX, centerY, labelPaint)
|
||||
}
|
||||
}
|
||||
is Element.Tld -> {
|
||||
val tld = element.tld
|
||||
if (tld.isNotEmpty()) {
|
||||
val centerX = left + elementWidth / 2.0f
|
||||
val centerY = top + elementHeight / 2.0f + (tldPaint.textSize - tldPaint.descent()) / 2
|
||||
canvas.drawText(tld, centerX, centerY, tldPaint)
|
||||
}
|
||||
}
|
||||
is Element.Icon -> {
|
||||
val drawable = element.icon
|
||||
drawable.setTint(labelPaint.color)
|
||||
val drawableSize = (min(elementWidth, elementHeight) * 0.6f).toInt()
|
||||
val drawablePaddingLeft = ((elementWidth - drawableSize) / 2.0f).toInt()
|
||||
val drawablePaddingTop = ((elementHeight - drawableSize) / 2.0f).toInt()
|
||||
drawable.setBounds(
|
||||
left + drawablePaddingLeft,
|
||||
top + drawablePaddingTop,
|
||||
left + drawablePaddingLeft + drawableSize,
|
||||
top + drawablePaddingTop + drawableSize
|
||||
)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
currentElementIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Properties(
|
||||
var width: Int,
|
||||
var height: Int,
|
||||
var xOffset: Int,
|
||||
var yOffset: Int,
|
||||
var gravity: Int,
|
||||
var elements: MutableList<MutableList<Element>>,
|
||||
var activeElementIndex: Int,
|
||||
var labelTextSize: Float
|
||||
) {
|
||||
fun getElementOrNull(index: Int = activeElementIndex): Element? {
|
||||
if (index < 0) {
|
||||
return null
|
||||
}
|
||||
var cachedIndex = index
|
||||
elements.reversed().forEach { row ->
|
||||
if (cachedIndex >= row.size) {
|
||||
cachedIndex -= row.size
|
||||
} else {
|
||||
return row[cachedIndex]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Element(val adjustedIndex: Int) {
|
||||
class Label(val label: String, adjustedIndex: Int) : Element(adjustedIndex)
|
||||
class Tld(val tld: String, adjustedIndex: Int) : Element(adjustedIndex)
|
||||
class Icon(val icon: Drawable, adjustedIndex: Int) : Element(adjustedIndex)
|
||||
object Undefined : Element(-1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import dev.patrickgold.florisboard.ime.extension.Asset
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
|
||||
|
||||
/**
|
||||
* An object which maps each base key to its extended popups. This can be done for each
|
||||
* key variation. [KeyVariation.ALL] is always the fallback for each key.
|
||||
*/
|
||||
typealias PopupMapping = Map<KeyVariation, Map<String, PopupSet<KeyData>>>
|
||||
|
||||
/**
|
||||
* Class which contains an extended popup mapping to use for adding popups subtype based on the
|
||||
* keyboard layout.
|
||||
*
|
||||
* @property mapping The mapping of the base keys to their popups. See [PopupMapping] for more info.
|
||||
*/
|
||||
class PopupExtension(
|
||||
override val name: String,
|
||||
override val label: String = name,
|
||||
override val authors: List<String>,
|
||||
val mapping: PopupMapping
|
||||
) : Asset {
|
||||
companion object : Asset.Companion<PopupExtension> {
|
||||
override fun empty() = PopupExtension("", "", listOf(), mapOf())
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,32 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import android.view.MotionEvent
|
||||
import android.widget.FrameLayout
|
||||
import dev.patrickgold.florisboard.ime.core.PrefHelper
|
||||
import dev.patrickgold.florisboard.util.*
|
||||
|
||||
class KeyPopupExtendedView : FlexboxLayout {
|
||||
class PopupLayerView : FrameLayout {
|
||||
private val prefs: PrefHelper = PrefHelper.getDefaultInstance(context)
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
setBackgroundTintColor2(this, prefs.theme.keyPopupBgColor)
|
||||
super.onDraw(canvas)
|
||||
init {
|
||||
background = null
|
||||
isClickable = false
|
||||
isFocusable = false
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,15 @@
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import android.view.*
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiKeyData
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiKeyView
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiKeyboardView
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyView
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
|
||||
@@ -41,12 +35,13 @@ import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardView
|
||||
*
|
||||
* @property keyboardView Reference to the keyboard view to which this manager class belongs to.
|
||||
*/
|
||||
class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD) {
|
||||
|
||||
class PopupManager<T_KBD: View, T_KV: View>(
|
||||
private val keyboardView: T_KBD,
|
||||
private val popupLayerView: PopupLayerView?
|
||||
) {
|
||||
private var anchorLeft: Boolean = false
|
||||
private var anchorRight: Boolean = false
|
||||
private var anchorOffset: Int = 0
|
||||
private var activeExtIndex: Int? = null
|
||||
private val exceptionsForKeyCodes = listOf(
|
||||
KeyCode.ENTER,
|
||||
KeyCode.LANGUAGE_SWITCH,
|
||||
@@ -55,111 +50,87 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
)
|
||||
private var keyPopupWidth: Int
|
||||
private var keyPopupHeight: Int
|
||||
var keyPopupTextSize: Float = keyboardView.resources.getDimension(R.dimen.key_popup_textSize)
|
||||
private var keyPopupDiffX: Int = 0
|
||||
private val popupView: LinearLayout
|
||||
private val popupViewExt: FlexboxLayout
|
||||
private val popupView: PopupView
|
||||
private val popupViewExt: PopupExtendedView
|
||||
private var row0count: Int = 0
|
||||
private var row1count: Int = 0
|
||||
private var window: PopupWindow
|
||||
private var windowExt: PopupWindow
|
||||
|
||||
/** Is true if the preview popup is visible to the user, else false */
|
||||
val isShowingPopup: Boolean
|
||||
get() = popupView.visibility == View.VISIBLE
|
||||
get() = popupView.isShowing
|
||||
/** Is true if the extended popup is visible to the user, else false */
|
||||
val isShowingExtendedPopup: Boolean
|
||||
get() = windowExt.isShowing
|
||||
get() = popupViewExt.isShowing
|
||||
|
||||
companion object {
|
||||
const val POPUP_EXTENSION_PATH_REL: String = "ime/text/characters/extended_popups"
|
||||
}
|
||||
|
||||
init {
|
||||
keyPopupWidth = keyboardView.resources.getDimension(R.dimen.key_width).toInt()
|
||||
keyPopupHeight = keyboardView.resources.getDimension(R.dimen.key_height).toInt()
|
||||
popupView = View.inflate(
|
||||
keyboardView.context,
|
||||
R.layout.key_popup, null
|
||||
) as LinearLayout
|
||||
popupView.visibility = View.INVISIBLE
|
||||
popupViewExt = View.inflate(
|
||||
keyboardView.context,
|
||||
R.layout.key_popup_extended, null
|
||||
) as FlexboxLayout
|
||||
window = createPopupWindow(popupView)
|
||||
windowExt = createPopupWindow(popupViewExt)
|
||||
popupView = PopupView(keyboardView.context)
|
||||
popupViewExt = PopupExtendedView(keyboardView.context)
|
||||
popupLayerView?.addView(popupView)
|
||||
popupLayerView?.addView(popupViewExt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a [KeyPopupExtendedSingleView] and preconfigure it.
|
||||
* Helper function to create a element for the extended popup and preconfigure it.
|
||||
*
|
||||
* @param keyView Reference to the keyView currently controlling the popup.
|
||||
* @param k The index of the key in the key data popup array.
|
||||
* @param isInitActive If it should initially be marked as active.
|
||||
* @param isWrapBefore If the [FlexboxLayout] should wrap before this view.
|
||||
* @return A preconfigured [KeyPopupExtendedSingleView].
|
||||
* @param adjustedIndex The index of the key in the key data popup array.
|
||||
* @return A preconfigured extended popup element.
|
||||
*/
|
||||
private fun createTextView(
|
||||
private fun createElement(
|
||||
keyView: T_KV,
|
||||
k: Int,
|
||||
isInitActive: Boolean = false,
|
||||
isWrapBefore: Boolean = false
|
||||
): KeyPopupExtendedSingleView? {
|
||||
val textView = KeyPopupExtendedSingleView(keyView.context, isInitActive)
|
||||
val lp = FlexboxLayout.LayoutParams(keyPopupWidth, keyView.measuredHeight)
|
||||
lp.isWrapBefore = isWrapBefore
|
||||
textView.layoutParams = lp
|
||||
textView.gravity = Gravity.CENTER
|
||||
val textSize = keyboardView.resources.getDimension(R.dimen.key_popup_textSize)
|
||||
if (keyView is KeyView) {
|
||||
when (keyView.data.popup[k].code) {
|
||||
KeyCode.SETTINGS -> {
|
||||
textView.iconDrawable = getDrawable(
|
||||
keyView.context, R.drawable.ic_settings
|
||||
)
|
||||
}
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT -> {
|
||||
textView.text = keyView.resources.getString(R.string.key__view_characters)
|
||||
}
|
||||
KeyCode.SWITCH_TO_MEDIA_CONTEXT -> {
|
||||
textView.iconDrawable = getDrawable(
|
||||
keyView.context, R.drawable.ic_sentiment_satisfied
|
||||
)
|
||||
}
|
||||
KeyCode.TOGGLE_ONE_HANDED_MODE -> {
|
||||
textView.iconDrawable = getDrawable(
|
||||
keyView.context, R.drawable.ic_keyboard_arrow_right
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
textView.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_PX, when (keyView.data.popup[k].code) {
|
||||
KeyCode.URI_COMPONENT_TLD,
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT -> textSize * 0.6f
|
||||
else -> textSize
|
||||
}
|
||||
)
|
||||
textView.text = keyView.getComputedLetter(keyView.data.popup[k])
|
||||
adjustedIndex: Int
|
||||
): PopupExtendedView.Element {
|
||||
return when (keyView) {
|
||||
is KeyView -> {
|
||||
when (keyView.data.popup[adjustedIndex].code) {
|
||||
KeyCode.SETTINGS -> {
|
||||
getDrawable(keyView.context, R.drawable.ic_settings)?.let {
|
||||
PopupExtendedView.Element.Icon(it, adjustedIndex)
|
||||
} ?: PopupExtendedView.Element.Undefined
|
||||
}
|
||||
KeyCode.SWITCH_TO_TEXT_CONTEXT -> {
|
||||
PopupExtendedView.Element.Label(
|
||||
keyView.resources.getString(R.string.key__view_characters), adjustedIndex
|
||||
)
|
||||
}
|
||||
KeyCode.SWITCH_TO_MEDIA_CONTEXT -> {
|
||||
getDrawable(keyView.context, R.drawable.ic_sentiment_satisfied)?.let {
|
||||
PopupExtendedView.Element.Icon(it, adjustedIndex)
|
||||
} ?: PopupExtendedView.Element.Undefined
|
||||
}
|
||||
KeyCode.URI_COMPONENT_TLD -> {
|
||||
PopupExtendedView.Element.Tld(
|
||||
keyView.data.popup[adjustedIndex].label, adjustedIndex
|
||||
)
|
||||
}
|
||||
KeyCode.TOGGLE_ONE_HANDED_MODE -> {
|
||||
getDrawable(keyView.context, R.drawable.ic_smartphone)?.let {
|
||||
PopupExtendedView.Element.Icon(it, adjustedIndex)
|
||||
} ?: PopupExtendedView.Element.Undefined
|
||||
}
|
||||
else -> {
|
||||
PopupExtendedView.Element.Label(
|
||||
keyView.getComputedLetter(keyView.data.popup[adjustedIndex]), adjustedIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
textView.text = keyView.data.popup[k].getCodePointsAsString()
|
||||
}
|
||||
return textView
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for a convenient way of creating a [PopupWindow].
|
||||
*
|
||||
* @param view The view to set as content view of the [PopupWindow].
|
||||
* @return A new [PopupWindow] already preconfigured and ready-to-go.
|
||||
*/
|
||||
private fun createPopupWindow(view: View): PopupWindow {
|
||||
return PopupWindow(keyboardView.context).apply {
|
||||
animationStyle = 0
|
||||
contentView = view
|
||||
enterTransition = null
|
||||
exitTransition = null
|
||||
isClippingEnabled = false
|
||||
isFocusable = false
|
||||
isTouchable = false
|
||||
setBackgroundDrawable(null)
|
||||
is EmojiKeyView -> {
|
||||
PopupExtendedView.Element.Label(
|
||||
keyView.data.popup[adjustedIndex].getCodePointsAsString(), adjustedIndex
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
PopupExtendedView.Element.Undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,12 +142,22 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
if (keyboardView is KeyboardView) {
|
||||
when (keyboardView.resources.configuration.orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
keyPopupWidth = (keyboardView.desiredKeyWidth * 0.6f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 3.0f).toInt()
|
||||
if (keyboardView.isSmartbarKeyboardView) {
|
||||
keyPopupWidth = (keyView.measuredWidth * 0.6f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 3.0f * 1.2f).toInt()
|
||||
} else {
|
||||
keyPopupWidth = (keyboardView.desiredKeyWidth * 0.6f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 3.0f).toInt()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
keyPopupWidth = (keyboardView.desiredKeyWidth * 1.1f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 2.5f).toInt()
|
||||
if (keyboardView.isSmartbarKeyboardView) {
|
||||
keyPopupWidth = (keyView.measuredWidth * 1.1f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 2.5f * 1.2f).toInt()
|
||||
} else {
|
||||
keyPopupWidth = (keyboardView.desiredKeyWidth * 1.1f).toInt()
|
||||
keyPopupHeight = (keyboardView.desiredKeyHeight * 2.5f).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keyboardView is EmojiKeyboardView) {
|
||||
@@ -192,36 +173,32 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
*
|
||||
* @param keyView Reference to the keyView currently controlling the popup.
|
||||
*/
|
||||
fun show(keyView: T_KV) {
|
||||
fun show(keyView: T_KV, keyHintMode: KeyHintMode) {
|
||||
if (keyView is KeyView && keyView.data.code <= KeyCode.SPACE) {
|
||||
return
|
||||
}
|
||||
|
||||
calc(keyView)
|
||||
|
||||
val keyPopupX = keyPopupDiffX
|
||||
val keyPopupY = -keyPopupHeight
|
||||
if (window.isShowing) {
|
||||
window.update(keyView, keyPopupX, keyPopupY, keyPopupWidth, keyPopupHeight)
|
||||
} else {
|
||||
window.width = keyPopupWidth
|
||||
window.height = keyPopupHeight
|
||||
window.showAsDropDown(keyView, keyPopupX, keyPopupY, Gravity.NO_GRAVITY)
|
||||
}
|
||||
if (keyView is KeyView) {
|
||||
popupView.findViewById<TextView>(R.id.key_popup_text)?.text = keyView.getComputedLetter()
|
||||
popupView.findViewById<ImageView>(R.id.key_popup_threedots)?.visibility = when {
|
||||
keyView.data.popup.isEmpty() -> View.INVISIBLE
|
||||
else -> View.VISIBLE
|
||||
popupView.properties.apply {
|
||||
width = keyPopupWidth
|
||||
height = keyPopupHeight
|
||||
xOffset = keyPopupDiffX
|
||||
yOffset = -keyPopupHeight
|
||||
innerLabelFactor = 0.4f
|
||||
label = when (keyView) {
|
||||
is KeyView -> keyView.getComputedLetter()
|
||||
is EmojiKeyView -> keyView.data.getCodePointsAsString()
|
||||
else -> ""
|
||||
}
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
popupView.findViewById<TextView>(R.id.key_popup_text)?.text = keyView.data.getCodePointsAsString()
|
||||
popupView.findViewById<ImageView>(R.id.key_popup_threedots)?.visibility = when {
|
||||
keyView.data.popup.isEmpty() -> View.INVISIBLE
|
||||
else -> View.VISIBLE
|
||||
labelTextSize = keyPopupTextSize
|
||||
shouldIndicateExtendedPopups = when (keyView) {
|
||||
is KeyView -> keyView.data.popup.size(keyHintMode) > 0
|
||||
is EmojiKeyView -> keyView.data.popup.isNotEmpty()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
popupView.visibility = View.VISIBLE
|
||||
popupView.show(keyView)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +222,7 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
*
|
||||
* @param keyView Reference to the keyView currently controlling the popup.
|
||||
*/
|
||||
fun extend(keyView: T_KV) {
|
||||
fun extend(keyView: T_KV, keyHintMode: KeyHintMode) {
|
||||
if (keyView is KeyView && keyView.data.code <= KeyCode.SPACE
|
||||
&& !exceptionsForKeyCodes.contains(keyView.data.code)) {
|
||||
return
|
||||
@@ -256,21 +233,17 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
|
||||
// Anchor left if keyView is in left half of keyboardView, else anchor right
|
||||
if (keyView is KeyView) {
|
||||
anchorLeft = keyView.x < keyboardView.measuredWidth / 2
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
val hsv = (keyView.parent.parent as HorizontalScrollView)
|
||||
anchorLeft = (keyView.x - hsv.scrollX) < keyboardView.measuredWidth / 2
|
||||
}
|
||||
anchorLeft = keyView.x < keyboardView.measuredWidth / 2
|
||||
anchorRight = !anchorLeft
|
||||
|
||||
// Determine key counts for each row
|
||||
val n = when (keyView) {
|
||||
is KeyView -> keyView.data.popup.size
|
||||
is KeyView -> keyView.data.popup.size(keyHintMode)
|
||||
is EmojiKeyView -> keyView.data.popup.size
|
||||
else -> 0
|
||||
}
|
||||
when {
|
||||
n <= 0 -> return
|
||||
n <= 5 -> {
|
||||
row1count = 0
|
||||
row0count = n
|
||||
@@ -313,64 +286,106 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
|
||||
// Build UI
|
||||
popupViewExt.removeAllViews()
|
||||
val indices = when (keyView) {
|
||||
is KeyView -> keyView.data.popup.indices
|
||||
is EmojiKeyView -> keyView.data.popup.indices
|
||||
else -> IntRange(0, 0)
|
||||
popupViewExt.properties.elements.clear()
|
||||
val initUiIndex = when {
|
||||
anchorLeft -> anchorOffset + row1count
|
||||
anchorRight -> row0count - 1 - anchorOffset + row1count
|
||||
else -> 0
|
||||
}
|
||||
for (k in indices) {
|
||||
val isInitActive =
|
||||
anchorLeft && (k - row1count == anchorOffset) ||
|
||||
anchorRight && (k - row1count == row0count - 1 - anchorOffset)
|
||||
popupViewExt.addView(
|
||||
createTextView(
|
||||
keyView, k, isInitActive, (row1count > 0) && (k - row1count == 0)
|
||||
)
|
||||
)
|
||||
if (isInitActive) {
|
||||
activeExtIndex = k
|
||||
val popupIndices: IntArray
|
||||
val uiIndices = IntRange(0, (n - 1).coerceAtLeast(0))
|
||||
if (keyView is KeyView) {
|
||||
popupIndices = IntArray(n) { 0 }
|
||||
when (keyHintMode) {
|
||||
KeyHintMode.ENABLED_ACCENT_PRIORITY -> when {
|
||||
keyView.data.popup.main != null -> {
|
||||
popupIndices[initUiIndex] = PopupSet.MAIN_INDEX
|
||||
if (keyView.data.popup.hint != null) when {
|
||||
initUiIndex + 1 < n -> popupIndices[initUiIndex + 1] = PopupSet.HINT_INDEX
|
||||
initUiIndex - 1 >= 0 -> popupIndices[initUiIndex - 1] = PopupSet.HINT_INDEX
|
||||
}
|
||||
}
|
||||
keyView.data.popup.hint != null -> when {
|
||||
initUiIndex + 1 < n -> popupIndices[initUiIndex + 1] = PopupSet.HINT_INDEX
|
||||
initUiIndex - 1 >= 0 -> popupIndices[initUiIndex - 1] = PopupSet.HINT_INDEX
|
||||
else -> popupIndices[initUiIndex] = PopupSet.HINT_INDEX
|
||||
}
|
||||
}
|
||||
KeyHintMode.ENABLED_HINT_PRIORITY -> when {
|
||||
keyView.data.popup.hint != null -> {
|
||||
popupIndices[initUiIndex] = PopupSet.HINT_INDEX
|
||||
if (keyView.data.popup.main != null) when {
|
||||
initUiIndex + 1 < n -> popupIndices[initUiIndex + 1] = PopupSet.MAIN_INDEX
|
||||
initUiIndex - 1 >= 0 -> popupIndices[initUiIndex - 1] = PopupSet.MAIN_INDEX
|
||||
}
|
||||
}
|
||||
keyView.data.popup.main != null -> popupIndices[initUiIndex] = PopupSet.MAIN_INDEX
|
||||
}
|
||||
KeyHintMode.ENABLED_SMART_PRIORITY -> when {
|
||||
keyView.data.popup.main != null -> {
|
||||
popupIndices[initUiIndex] = PopupSet.MAIN_INDEX
|
||||
if (keyView.data.popup.hint != null) when {
|
||||
initUiIndex + 1 < n -> popupIndices[initUiIndex + 1] = PopupSet.HINT_INDEX
|
||||
initUiIndex - 1 >= 0 -> popupIndices[initUiIndex - 1] = PopupSet.HINT_INDEX
|
||||
}
|
||||
}
|
||||
keyView.data.popup.hint != null -> popupIndices[initUiIndex] = PopupSet.HINT_INDEX
|
||||
}
|
||||
KeyHintMode.DISABLED -> when {
|
||||
keyView.data.popup.main != null -> popupIndices[initUiIndex] = PopupSet.MAIN_INDEX
|
||||
}
|
||||
}
|
||||
var offset = 0
|
||||
for (uiIndex in uiIndices) {
|
||||
if (popupIndices[uiIndex] < 0) {
|
||||
offset++
|
||||
} else {
|
||||
popupIndices[uiIndex] = uiIndex - offset
|
||||
}
|
||||
}
|
||||
} else {
|
||||
popupIndices = IntArray(n) { it }
|
||||
}
|
||||
if (row1count > 0) {
|
||||
popupViewExt.properties.elements.add(mutableListOf())
|
||||
}
|
||||
popupViewExt.properties.elements.add(mutableListOf())
|
||||
for (uiIndex in uiIndices) {
|
||||
val rowIndex = if (uiIndex < row1count && row1count > 0) { 1 } else { 0 }
|
||||
popupViewExt.properties.elements[rowIndex].add(
|
||||
createElement(keyView, popupIndices[uiIndex])
|
||||
)
|
||||
}
|
||||
popupView.findViewById<ImageView>(R.id.key_popup_threedots)?.visibility = View.INVISIBLE
|
||||
|
||||
// Calculate layout params
|
||||
val extWidth = row0count * keyPopupWidth
|
||||
val extHeight = when {
|
||||
row1count > 0 -> keyView.measuredHeight * 2
|
||||
else -> keyView.measuredHeight
|
||||
}
|
||||
popupViewExt.justifyContent = if (anchorLeft) {
|
||||
JustifyContent.FLEX_START
|
||||
} else {
|
||||
JustifyContent.FLEX_END
|
||||
}
|
||||
if (popupViewExt.layoutParams == null) {
|
||||
popupViewExt.layoutParams = ViewGroup.LayoutParams(extWidth, extHeight)
|
||||
} else {
|
||||
popupViewExt.layoutParams.apply {
|
||||
width = extWidth
|
||||
height = extHeight
|
||||
}
|
||||
}
|
||||
row1count > 0 -> keyPopupHeight * 0.4f * 2.0f
|
||||
else -> keyPopupHeight * 0.4f
|
||||
}.toInt()
|
||||
val x = ((keyView.measuredWidth - keyPopupWidth) / 2) + when {
|
||||
anchorLeft -> -anchorOffset * keyPopupWidth
|
||||
anchorRight -> -extWidth + keyPopupWidth + anchorOffset * keyPopupWidth
|
||||
else -> 0
|
||||
}
|
||||
val y = -keyPopupHeight - when {
|
||||
row1count > 0 -> keyView.measuredHeight
|
||||
row1count > 0 -> (keyPopupHeight * 0.4f).toInt()
|
||||
else -> 0
|
||||
}
|
||||
|
||||
// Position and show popup window
|
||||
if (windowExt.isShowing) {
|
||||
windowExt.update(keyView, x, y, extWidth, extHeight)
|
||||
} else {
|
||||
windowExt.width = extWidth
|
||||
windowExt.height = extHeight
|
||||
windowExt.showAsDropDown(keyView, x, y, Gravity.NO_GRAVITY)
|
||||
popupViewExt.properties.apply {
|
||||
width = extWidth
|
||||
height = extHeight
|
||||
xOffset = x
|
||||
yOffset = y
|
||||
gravity = if (anchorLeft) { Gravity.START } else { Gravity.END }
|
||||
labelTextSize = keyPopupTextSize
|
||||
activeElementIndex = initUiIndex
|
||||
}
|
||||
popupViewExt.show(keyView)
|
||||
|
||||
popupView.properties.shouldIndicateExtendedPopups = false
|
||||
popupView.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -393,7 +408,7 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
return false
|
||||
}
|
||||
|
||||
activeExtIndex = when {
|
||||
popupViewExt.properties.activeElementIndex = when {
|
||||
anchorLeft -> when {
|
||||
// check if out of boundary on x-axis
|
||||
event.x < keyPopupDiffX - (anchorOffset + 1) * keyPopupWidth ||
|
||||
@@ -439,24 +454,7 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
}
|
||||
else -> -1
|
||||
}
|
||||
|
||||
if (keyView is KeyView) {
|
||||
for (k in keyView.data.popup.indices) {
|
||||
val view = popupViewExt.getChildAt(k)
|
||||
if (view != null) {
|
||||
val textView = view as KeyPopupExtendedSingleView
|
||||
textView.isActive = k == activeExtIndex
|
||||
}
|
||||
}
|
||||
} else if (keyView is EmojiKeyView) {
|
||||
for (k in keyView.data.popup.indices) {
|
||||
val view = popupViewExt.getChildAt(k)
|
||||
if (view != null) {
|
||||
val textView = view as KeyPopupExtendedSingleView
|
||||
textView.isActive = k == activeExtIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
popupViewExt.invalidate()
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -471,7 +469,12 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
*/
|
||||
fun getActiveKeyData(keyView: T_KV): KeyData? {
|
||||
return if (keyView is KeyView) {
|
||||
keyView.data.popup.getOrNull(activeExtIndex ?: -1) ?: keyView.data
|
||||
val element = popupViewExt.properties.getElementOrNull()
|
||||
if (element != null) {
|
||||
keyView.data.popup.getOrNull(element.adjustedIndex) ?: keyView.data
|
||||
} else {
|
||||
keyView.data
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -487,7 +490,12 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
*/
|
||||
fun getActiveEmojiKeyData(keyView: T_KV): EmojiKeyData? {
|
||||
return if (keyView is EmojiKeyView) {
|
||||
keyView.data.popup.getOrNull(activeExtIndex ?: -1) ?: keyView.data
|
||||
val element = popupViewExt.properties.getElementOrNull()
|
||||
if (element != null) {
|
||||
keyView.data.popup.getOrNull(element.adjustedIndex) ?: keyView.data
|
||||
} else {
|
||||
keyView.data
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -497,12 +505,9 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
* Hides the key preview popup as well as the extended popup.
|
||||
*/
|
||||
fun hide() {
|
||||
popupView.visibility = View.INVISIBLE
|
||||
if (windowExt.isShowing) {
|
||||
windowExt.dismiss()
|
||||
}
|
||||
|
||||
activeExtIndex = null
|
||||
popupView.hide()
|
||||
popupViewExt.hide()
|
||||
popupViewExt.properties.activeElementIndex = -1
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,13 +515,10 @@ class KeyPopupManager<T_KBD: View, T_KV: View>(private val keyboardView: T_KBD)
|
||||
* is closing.
|
||||
*/
|
||||
fun dismissAllPopups() {
|
||||
if (window.isShowing) {
|
||||
window.dismiss()
|
||||
}
|
||||
if (windowExt.isShowing) {
|
||||
windowExt.dismiss()
|
||||
}
|
||||
|
||||
activeExtIndex = null
|
||||
popupView.hide()
|
||||
popupLayerView?.removeView(popupView)
|
||||
popupViewExt.hide()
|
||||
popupViewExt.properties.activeElementIndex = -1
|
||||
popupLayerView?.removeView(popupViewExt)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Patrick Goldinger
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
|
||||
/**
|
||||
* A popup set for a single key. This set describes, if the key has a [hint] character,
|
||||
* a [main] character and other [relevant] popups.
|
||||
*
|
||||
* Note, that a hint character should **never** be set in a json extended popup file, rather it
|
||||
* should only be dynamically set by the LayoutManager.
|
||||
*
|
||||
* The order in which these defined popups will be shown depends on the current [KeyHintMode],
|
||||
* al well as the calculations made by the KeyPopupManager.
|
||||
*
|
||||
* The popup set can be accessed like an array with the addition that negative indexes defined
|
||||
* within this companion object are allowed (as long as the corresponding [hint] or [main]
|
||||
* character is *not* null).
|
||||
*/
|
||||
class PopupSet<T> (
|
||||
var hint: T? = null,
|
||||
var main: T? = null,
|
||||
var relevant: List<T> = listOf()
|
||||
) : Collection<T> {
|
||||
companion object {
|
||||
const val HINT_INDEX: Int = -2
|
||||
const val MAIN_INDEX: Int = -1
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = if (hint != null) { 1 } else { 0 } + if (main != null) { 1 } else { 0 } +
|
||||
relevant.size
|
||||
|
||||
fun size(keyHintMode: KeyHintMode): Int {
|
||||
return if (keyHintMode == KeyHintMode.DISABLED && hint != null) {
|
||||
size - 1
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(index: Int): T {
|
||||
val item = getOrNull(index)
|
||||
if (item == null) {
|
||||
throw IndexOutOfBoundsException(
|
||||
"Specified index $index is not an valid entry in this PopupSet!"
|
||||
)
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
fun getOrNull(index: Int): T? {
|
||||
if (index >= relevant.size || index < HINT_INDEX) {
|
||||
return null
|
||||
}
|
||||
return when (index) {
|
||||
HINT_INDEX -> hint
|
||||
MAIN_INDEX -> main
|
||||
else -> relevant.getOrNull(index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun contains(element: T): Boolean {
|
||||
return hint == element || main == element || relevant.contains(element)
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean {
|
||||
for (element in elements) {
|
||||
if (!contains(element)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return size == 0
|
||||
}
|
||||
|
||||
fun merge(other: PopupSet<T>) {
|
||||
val tempRelevant = relevant.toMutableList()
|
||||
tempRelevant.addAll(other.relevant)
|
||||
other.hint?.let {
|
||||
if (hint == null) {
|
||||
hint = it
|
||||
} else {
|
||||
tempRelevant.add(it)
|
||||
}
|
||||
}
|
||||
other.main?.let {
|
||||
if (main == null) {
|
||||
main = it
|
||||
} else {
|
||||
tempRelevant.add(it)
|
||||
}
|
||||
}
|
||||
relevant = tempRelevant.toList()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user