Compare commits

...

70 Commits

Author SHA1 Message Date
Patrick Goldinger
166734757f Release v0.3.15 2022-04-10 13:57:55 +02:00
Patrick Goldinger
97f401371f Fix crash in key reset logic (#1771) 2022-04-10 12:26:21 +02:00
Patrick Goldinger
b012e5377b Release v0.3.15-rc02 2022-04-09 19:19:54 +02:00
florisboard-bot
3b18dc33d8 Update translations from Crowdin 2022-04-09 19:07:29 +02:00
Patrick Goldinger
d798f01576 Merge pull request #1769 from florisboard/fix-key-visuals-and-popups-getting-stuck
Fix key visuals and popups getting stuck
2022-04-09 17:35:55 +02:00
Patrick Goldinger
55281bd7d6 Merge pull request #1768 from florisboard/add-guardedbylock-and-fix-glide-issues
Add guardedbylock and fix glide issues
2022-04-09 17:34:59 +02:00
Patrick Goldinger
88e9bde0a9 Fix popup stays active when using multitouch (#1450) 2022-04-09 17:26:11 +02:00
Patrick Goldinger
54d5a16761 Fix keys can get stuck with active visuals (#1446) 2022-04-09 16:43:53 +02:00
Patrick Goldinger
701da50479 Add GuardedByLock to prevent crash (#1632) 2022-04-09 16:02:06 +02:00
Patrick Goldinger
b43b6aa2d0 Remove unnecessary classes and methods in common 2022-04-09 13:07:37 +02:00
Patrick Goldinger
4279e9d100 Fix Unicode non-spacing marks for Thai (#1759) 2022-04-08 20:06:36 +02:00
M-Koushan
e5bd979880 Add German (Gboard) layout (#1737) & Fix Persian layout issue (#1746) 2022-04-08 18:48:00 +02:00
Patrick Goldinger
9a48169bf3 Fix delete key swipe right deleting left words (#1765) 2022-04-08 17:42:47 +02:00
Patrick Goldinger
99cfd99815 Merge pull request #1763 from florisboard/fix-extracted-input-ui-issues
Fix extracted input UI issues
2022-04-08 17:23:19 +02:00
Hayleia
e8f082d885 Fix Korean composition of medial+final and final+final (#1757) 2022-04-07 20:33:45 +02:00
Patrick Goldinger
b63f475a8c Fix NPE crash in extracted input UI (#1758) 2022-04-07 20:12:20 +02:00
Patrick Goldinger
e183f10969 Improve devtools overlay UI and remove unnecessary code 2022-04-06 23:57:57 +02:00
Patrick Goldinger
c1e624b9a0 Fix input view + extracted input UI layouting issues (#1760) 2022-04-06 22:51:51 +02:00
Patrick Goldinger
2d87d0e4d3 Release v0.3.15-rc01 2022-04-05 23:59:07 +02:00
florisboard-bot
bcf1bffc24 Update translations from Crowdin 2022-04-05 23:45:52 +02:00
Patrick Goldinger
f388e8811e Add support for videos on clipboard history (#1721) 2022-04-05 23:42:12 +02:00
Patrick Goldinger
7ededa6293 Merge pull request #1756 from florisboard/quick-glide-fixes
Fix glide typing state and drawing bugs
2022-04-05 22:21:49 +02:00
Patrick Goldinger
7d9e1cf2b5 Fix glide trail not drawing if popups disabled (#1704) 2022-04-05 22:12:52 +02:00
Patrick Goldinger
299f581609 Fix glide not initializing if disabled on app start (#1703) 2022-04-05 21:33:46 +02:00
Patrick Goldinger
c38d4ed90b Merge pull request #1749 from florisboard/re-add-other-default-themes
Re-implement other default themes
2022-04-05 19:43:52 +02:00
Patrick Goldinger
647bc659d7 Decrease visual key height for borderless themes 2022-04-05 19:25:20 +02:00
Patrick Goldinger
61eb09e611 Re-add Pure Night and all borderless themes (#1702) 2022-04-05 19:08:28 +02:00
Patrick Goldinger
1a4118d29a Fix space bar ignoring "No action" setting (#1706) 2022-04-04 23:04:43 +02:00
Denys Honsiorovskyi
48655b3771 Ukrainian popup mappings improvements (#1739)
* Ukrainian popup mappings improvements

* Make all letters relevant not to override default symbols

* Update extension.json

* Remove extra domains: they look ugly
2022-04-04 22:41:53 +02:00
pjtsearch
17649c44bf Fix փ missing in Armenian layouts (#1741)
Co-authored-by: PJTSearch <pjtsignups@gmail.com>
2022-04-04 22:34:31 +02:00
Patrick Goldinger
7cbb19ddcb Add support for Chinese (Simplified) (zh-CN) 2022-04-04 20:04:25 +02:00
Patrick Goldinger
e13ac7c689 Add support for locale display language in devtools 2022-04-04 20:03:58 +02:00
Patrick Goldinger
941733cdc0 Merge pull request #1734 from florisboard/add-extracted-landscape-input-layout
Add support for ExtractedLandscapeInput
2022-04-03 15:16:44 +02:00
Patrick Goldinger
a25289a856 Fix ExtractedLandscapeInput UI themeing 2022-04-02 20:46:59 +02:00
Patrick Goldinger
7bcfeca872 Improve ExtractedLandscapeInput UI and fix compatibility
... with Samsung devices.
2022-04-02 20:01:45 +02:00
Patrick Goldinger
240ebc499a Improve debug summarization of EditorInfo 2022-04-02 17:09:10 +02:00
Patrick Goldinger
831882f419 Add base for ExtractedLandscapeInput 2022-04-01 20:24:08 +02:00
Patrick Goldinger
e0e5259b4c Remove deprecated shift and caps flag from KeyboardState 2022-04-01 18:37:04 +02:00
Patrick Goldinger
33cd2b5d01 Release v0.3.15-beta02 2022-03-31 20:35:03 +02:00
florisboard-bot
d3dda86966 Update translations from Crowdin 2022-03-31 20:21:27 +02:00
Patrick Goldinger
59aa5cdb33 Fix Restore screen too restrictive about media type (#1707) 2022-03-31 20:08:31 +02:00
Patrick Goldinger
d26e820492 Possible fix for setup screen system observer failing (#1731) 2022-03-31 19:49:33 +02:00
Patrick Goldinger
b8b1b04c7e Decrease vertical padding usage in setup screen (#1364) 2022-03-31 19:48:56 +02:00
Patrick Goldinger
26b4acc894 Add limit to clipboard history text item preview (#1730) 2022-03-31 19:19:40 +02:00
Patrick Goldinger
12c4220544 Merge pull request #1720 from florisboard/improve-theme-logic-and-ui
Improve theme logic, appearance and stylesheet editor UI
2022-03-31 18:55:43 +02:00
Patrick Goldinger
5fc4f5ba60 Improve wording of code param in stylesheet editor 2022-03-31 01:39:07 +02:00
Patrick Goldinger
03ea9bcb76 Add key code recording tool in stylesheet editor 2022-03-31 01:03:59 +02:00
Patrick Goldinger
a04c44df98 Improve common Android Toast extension funs 2022-03-30 22:06:55 +02:00
Patrick Goldinger
b1431c7e51 Add key code preview box in stylesheet rule dialog 2022-03-29 01:32:46 +02:00
Patrick Goldinger
c09719ffd6 Fix emoji key (and popup) font color ignoring current theme 2022-03-28 19:49:39 +02:00
Patrick Goldinger
16149d95a1 Fix key shape clipping contents (#1710) 2022-03-28 19:39:37 +02:00
Patrick Goldinger
658e43da9c Move fontSizeMultiplier() to AppPrefs directly 2022-03-28 19:29:56 +02:00
M-Koushan
66ddb451ab Add additional Persian keyboard layout (#1683)
* Adding new layout Persian2

* Fix whitespace issues and charactersMod missing

* Fix issues in fa2.json

Co-authored-by: M-Koushan <koushan405@gmail.com>
Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-03-27 13:09:31 +02:00
Patrick Goldinger
f135513f3e Add support for A12 VibratiorManager 2022-03-26 20:00:52 +01:00
Patrick Goldinger
322dfa717b Fix language names not titlecase for some locales (#1686) 2022-03-26 11:26:56 +01:00
Patrick Goldinger
bfe7852bdf Release v0.3.15-beta01 2022-03-25 19:54:49 +01:00
florisboard-bot
45fe2f311e Update translations from Crowdin 2022-03-25 19:43:46 +01:00
blucin
f73daa2b00 Add ColemakDH keyboard layout (#1401)
* Added ColemakDH layout

* Adjust ColemakDH to new Flex extension format

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-03-25 19:39:22 +01:00
Nijat Ismayilzada
4e8ff9ec14 Add Azerbaijani keyboard layout (#1639)
* Add Azerbaijani keyboard layout

* Add subtype preset for Azerbaijani

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-03-25 19:24:18 +01:00
Amir Yalon
a96fc84fc1 Add Hebrew SI-1452 niqqud and some punctuation marks (#1413)
Based on information from https://en.wikipedia.org/wiki/Hebrew_keyboard

Combining characters don’t display well on their own. In the source code, they are avoided altogether by using JSON
escape codes like `\u05c2`, while in labels a placeholder letter is used. The letter ס was chosen because it is hollow,
and the letter ש must be used for its dots because other letters take the HEBREW POINT SHIN DOT on the wrong side when
combined.
2022-03-25 17:55:34 +01:00
Patrick Goldinger
e62ddc37dd Disable forceDarkAllowed in IME base theme (#1694) 2022-03-25 17:25:12 +01:00
Patrick Goldinger
06cfa34a4b Remove popups from telpad layout (#1044) 2022-03-25 16:51:08 +01:00
Patrick Goldinger
ef849dfefd Add alphabetic letters to phone digits in telpad layout (#355) 2022-03-25 16:51:08 +01:00
pjtsearch
091d43520e Add Armenian keyboard layouts (#1654)
* feat: Add Western Armenian layouts, popup mapping, and currency

* feat: Add Eastern Armenian layouts, popup mapping, and currency

* fix: Fix Armenian popup mapping

* fix: Add yev to Armenian popup mapping

* fix: Fix Western Armenian language tag

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>

* fix: Differentiate Western layout name

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>

* fix: Differentiate Eastern layout name

Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>

Co-authored-by: PJTSearch <pjtsignups@gmail.com>
Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-03-25 00:07:29 +01:00
Patrick Goldinger
95b6b1bbf9 Fix keyboard ignoring number row height in symbols (#1658) 2022-03-24 22:33:39 +01:00
Patrick Goldinger
021014e870 Change layout direction of Smartbar and Emoji to forced LTR (#1673) 2022-03-24 22:10:20 +01:00
Kostas Giapis
aaa4fbae7a Fix main popup for "ﺍ" ‎(#1571) 2022-03-24 21:57:56 +01:00
Patrick Goldinger
78b645d820 Fix media and clipboard ignoring number row in height calc (#1672) 2022-03-24 20:37:42 +01:00
GoRaN
61bd6752e3 Fix TLDs of Arabic and Urdu-Phonetic (#1680) 2022-03-24 19:18:57 +01:00
Patrick Goldinger
e4e10f5c72 Fix Arabic combining characters not displaying correctly (#1679) 2022-03-24 07:43:18 +01:00
114 changed files with 4495 additions and 1158 deletions

View File

@@ -31,8 +31,8 @@ android {
applicationId = "dev.patrickgold.florisboard"
minSdk = 23
targetSdk = 31
versionCode = 75
versionName = "0.3.14"
versionCode = 80
versionName = "0.3.15"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -128,9 +128,9 @@
android:label="@string/crash_dialog__title"
android:theme="@style/CrashDialogTheme"/>
<!-- Clipboard Image File Provider -->
<!-- Clipboard Media File Provider -->
<provider
android:name="dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardImagesProvider"
android:name="dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardMediaProvider"
android:authorities="${applicationId}.provider.clipboard"
android:grantUriPermissions="true"
android:exported="false">

View File

@@ -9,6 +9,18 @@
"license": "apache-2.0"
},
"currencySets": [
{
"id": "armenian_dram",
"label": "Armenian dram (֏)",
"slots": [
{ "code": 1423, "label": "֏" },
{ "code": 36, "label": "$" },
{ "code": 8364, "label": "€" },
{ "code": 162, "label": "¢" },
{ "code": 163, "label": "£" },
{ "code": 165, "label": "¥" }
]
},
{
"id": "azerbaijani_manat",
"label": "Azerbaijani manat (₼)",

View File

@@ -17,6 +17,26 @@
"direction": "rtl",
"modifier": "org.florisboard.layouts:arabic"
},
{
"id": "western_armenian",
"label": "Armenian (Western)",
"authors": [ "PJTSearch" ],
"direction": "ltr",
"modifier": "org.florisboard.layouts:armenian"
},
{
"id": "eastern_armenian",
"label": "Armenian (Eastern)",
"authors": [ "PJTSearch" ],
"direction": "ltr",
"modifier": "org.florisboard.layouts:armenian"
},
{
"id": "azerbaijani",
"label": "Azerbaijani",
"authors": [ "nijatismayilzada" ],
"direction": "ltr"
},
{
"id": "azerty",
"label": "AZERTY",
@@ -72,6 +92,12 @@
"authors": [ "patrickgold" ],
"direction": "ltr"
},
{
"id": "colemak_dh",
"label": "ColemakDH",
"authors": [ "blucin" ],
"direction": "ltr"
},
{
"id": "danish",
"label": "Danish (QWERTY)",
@@ -109,6 +135,12 @@
"authors": [ "mahmoudk1000" ],
"direction": "ltr"
},
{
"id": "german2",
"label": "German (GBoard)",
"authors": [ "M-Koushan" ],
"direction": "rtl"
},
{
"id": "greek",
"label": "Ελληνικά",
@@ -223,6 +255,13 @@
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian"
},
{
"id": "persian2",
"label": "Persian2",
"authors": [ "M-Koushan" ],
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian2"
},
{
"id": "qwerty",
"label": "QWERTY",
@@ -346,6 +385,12 @@
"authors": [ "patrickgold" ],
"direction": "ltr"
},
{
"id": "armenian",
"label": "Armenian",
"authors": [ "PJTSearch" ],
"direction": "ltr"
},
{
"id": "arabic",
"label": "Arabic",
@@ -387,6 +432,12 @@
"label": "Persian",
"authors": [ "PHELAT" ],
"direction": "rtl"
},
{
"id": "persian2",
"label": "Persian2",
"authors": [ "M-Koushan" ],
"direction": "rtl"
}
],
"extension": [
@@ -540,6 +591,13 @@
}
],
"symbols": [
{
"id": "armenian",
"label": "Armenian",
"authors": [ "PJTSearch" ],
"direction": "ltr",
"modifier": "org.florisboard.layouts:armenian"
},
{
"id": "cjk",
"label": "CJK",
@@ -592,6 +650,12 @@
"authors": [ "patrickgold" ],
"direction": "ltr"
},
{
"id": "armenian",
"label": "Armenian",
"authors": [ "PJTSearch" ],
"direction": "ltr"
},
{
"id": "cjk",
"label": "CJK",

View File

@@ -0,0 +1,46 @@
[
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "case_selector",
"lower": { "code": 105, "label": "i" },
"upper": { "code": 304, "label": "İ" }
},
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 287, "label": "ğ" }
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "case_selector",
"lower": { "code": 305, "label": "ı" },
"upper": { "code": 73, "label": "I" }
},
{ "$": "auto_text_key", "code": 601, "label": "ə" }
],
[
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 351, "label": "ş" }
]
]

View File

@@ -0,0 +1,46 @@
[
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "case_selector",
"lower": { "code": 59, "label": ";", "popup": {
"relevant": [
{ "code": 58, "label": ":" }
]
} },
"upper": { "code": 58, "label": ":", "popup": {
"relevant": [
{ "code": 59, "label": ";" }
]
} }
}
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" }
],
[
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 104, "label": "h" }
]
]

View File

@@ -0,0 +1,48 @@
[
[
{ "$": "auto_text_key", "code": 1393, "label": "ձ" },
{ "$": "auto_text_key", "code": 1397, "label": "յ" },
{ "$": "auto_text_key", "code": 1413, "label": "օ" },
{ "$": "auto_text_key", "code": 1404, "label": "ռ" },
{ "$": "auto_text_key", "code": 1386, "label": "ժ" },
{ "$": "auto_text_key", "code": 1401, "label": "չ" },
{ "$": "auto_text_key", "code": 1403, "label": "ջ" },
{ "$": "auto_text_key", "code": 1411, "label": "փ" },
{ "$": "auto_text_key", "code": 1394, "label": "ղ" },
{ "$": "auto_text_key", "code": 1390, "label": "ծ" }
],
[
{ "$": "auto_text_key", "code": 1389, "label": "խ" },
{ "$": "auto_text_key", "code": 1410, "label": "ւ" },
{ "$": "auto_text_key", "code": 1383, "label": "է" },
{ "$": "auto_text_key", "code": 1408, "label": "ր" },
{ "$": "auto_text_key", "code": 1407, "label": "տ" },
{ "$": "auto_text_key", "code": 1381, "label": "ե" },
{ "$": "auto_text_key", "code": 1384, "label": "ը" },
{ "$": "auto_text_key", "code": 1387, "label": "ի" },
{ "$": "auto_text_key", "code": 1400, "label": "ո" },
{ "$": "auto_text_key", "code": 1402, "label": "պ" }
],
[
{ "$": "auto_text_key", "code": 1377, "label": "ա" },
{ "$": "auto_text_key", "code": 1405, "label": "ս" },
{ "$": "auto_text_key", "code": 1380, "label": "դ" },
{ "$": "auto_text_key", "code": 1414, "label": "ֆ" },
{ "$": "auto_text_key", "code": 1412, "label": "ք" },
{ "$": "auto_text_key", "code": 1392, "label": "հ" },
{ "$": "auto_text_key", "code": 1395, "label": "ճ" },
{ "$": "auto_text_key", "code": 1391, "label": "կ" },
{ "$": "auto_text_key", "code": 1388, "label": "լ" },
{ "$": "auto_text_key", "code": 1385, "label": "թ" }
],
[
{ "$": "auto_text_key", "code": 1382, "label": "զ" },
{ "$": "auto_text_key", "code": 1409, "label": "ց" },
{ "$": "auto_text_key", "code": 1379, "label": "գ" },
{ "$": "auto_text_key", "code": 1406, "label": "վ" },
{ "$": "auto_text_key", "code": 1378, "label": "բ" },
{ "$": "auto_text_key", "code": 1398, "label": "ն" },
{ "$": "auto_text_key", "code": 1396, "label": "մ" },
{ "$": "auto_text_key", "code": 1399, "label": "շ" }
]
]

View File

@@ -0,0 +1,37 @@
[
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
],
[
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]

View File

@@ -2,44 +2,85 @@
[
{ "code": 39, "label": "'", "popup": {
"relevant": [
{ "code": 1474, "label": "ש\u05c2" },
{ "code": 1467, "label": "ס\u05bb" },
{ "code": 1523, "label": "׳" },
{ "code": 1524, "label": "״" },
{ "code": 34, "label": "\"" },
{ "code": 96, "label": "`" }
]
} },
{ "code": 45, "label": "-", "popup": {
"relevant": [
{ "code": 1470, "label": "־" },
{ "code": 1473, "label": "ש\u05c1" },
{ "code": 95, "label": "_" }
]
} },
{ "code": 1511, "label": "ק" },
{ "code": 1511, "label": "ק", "popup": {
"relevant": [
{ "code": 1464, "label": "ס\u05b8" },
{ "code": 1459, "label": "ס\u05b3" }
]
} },
{ "code": 1512, "label": "ר" },
{ "code": 1488, "label": "א" },
{ "code": 1496, "label": "ט" },
{ "code": 1493, "label": "ו" },
{ "code": 1493, "label": "ו", "popup": {
"relevant": [
{ "code": 1465, "label": "ס\u05b9" }
]
} },
{ "code": 1503, "label": "ן" },
{ "code": 1501, "label": "ם" },
{ "code": 1508, "label": "פ" }
{ "code": 1508, "label": "פ", "popup": {
"relevant": [
{ "code": 1463, "label": "ס\u05b7" },
{ "code": 1458, "label": "ס\u05b2" }
]
} }
],
[
{ "code": 1513, "label": "ש" },
{ "code": 1491, "label": "ד" },
{ "code": 1513, "label": "ש", "popup": {
"relevant": [
{ "code": 1456, "label": "ס\u05b0" }
]
} },
{ "code": 1491, "label": "ד", "popup": {
"relevant": [
{ "code": 1468, "label": "ס\u05bc" }
]
} },
{ "code": 1490, "label": "ג" },
{ "code": 1499, "label": "כ" },
{ "code": 1506, "label": "ע" },
{ "code": 1497, "label": "י" },
{ "code": 1495, "label": "ח" },
{ "code": 1495, "label": "ח", "popup": {
"relevant": [
{ "code": 1460, "label": "ס\u05b4" }
]
} },
{ "code": 1500, "label": "ל" },
{ "code": 1498, "label": "ך" },
{ "code": 1507, "label": "ף" }
],
[
{ "code": 1494, "label": "ז" },
{ "code": 1505, "label": "ס" },
{ "code": 1505, "label": "ס", "popup": {
"relevant": [
{ "code": 1462, "label": "ס\u05b6" },
{ "code": 1457, "label": "ס\u05b1" }
]
} },
{ "code": 1489, "label": "ב" },
{ "code": 1492, "label": "ה" },
{ "code": 1504, "label": "נ" },
{ "code": 1502, "label": "מ" },
{ "code": 1510, "label": "צ" },
{ "code": 1510, "label": "צ", "popup": {
"relevant": [
{ "code": 1461, "label": "ס\u05b5" }
]
} },
{ "code": 1514, "label": "ת" },
{ "code": 1509, "label": "ץ" }
]

View File

@@ -0,0 +1,36 @@
[
[
{ "code": 1589, "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": 1670, "label": "چ" }
],
[
{ "code": 1588, "label": "ش" },
{ "code": 1587, "label": "س" },
{ "code": 1740, "label": "ی" },
{ "code": 1576, "label": "ب" },
{ "code": 1604, "label": "ل" },
{ "code": 1575, "label": "ا" },
{ "code": 1578, "label": "ت" },
{ "code": 1606, "label": "ن" },
{ "code": 1605, "label": "م" },
{ "code": 1705, "label": "ک" }
],
[
{ "code": 8204, "label": "half_space" },
{ "code": 1591, "label": "ط" },
{ "code": 1586, "label": "ز" },
{ "code": 1585, "label": "ر" },
{ "code": 1584, "label": "ذ" },
{ "code": 1583, "label": "د" },
{ "code": 1608, "label": "و" },
{ "code": 1711, "label": "گ" }
]
]

View File

@@ -0,0 +1,49 @@
[
[
{ "$": "auto_text_key", "code": 1393, "label": "ձ" },
{ "$": "auto_text_key", "code": 1397, "label": "յ" },
{ "$": "auto_text_key", "code": 1413, "label": "օ" },
{ "$": "auto_text_key", "code": 1404, "label": "ռ" },
{ "$": "auto_text_key", "code": 1386, "label": "ժ" },
{ "$": "auto_text_key", "code": 1401, "label": "չ" },
{ "$": "auto_text_key", "code": 1403, "label": "ջ" },
{ "$": "auto_text_key", "code": 1411, "label": "փ" },
{ "$": "auto_text_key", "code": 1394, "label": "ղ" },
{ "$": "auto_text_key", "code": 1390, "label": "ծ" }
],
[
{ "$": "auto_text_key", "code": 1389, "label": "խ" },
{ "$": "auto_text_key", "code": 1406, "label": "վ" },
{ "$": "auto_text_key", "code": 1383, "label": "է" },
{ "$": "auto_text_key", "code": 1408, "label": "ր" },
{ "$": "auto_text_key", "code": 1380, "label": "դ" },
{ "$": "auto_text_key", "code": 1381, "label": "ե" },
{ "$": "auto_text_key", "code": 1384, "label": "ը" },
{ "$": "auto_text_key", "code": 1387, "label": "ի" },
{ "$": "auto_text_key", "code": 1400, "label": "ո" },
{ "$": "auto_text_key", "code": 1378, "label": "բ" }
],
[
{ "$": "auto_text_key", "code": 1377, "label": "ա" },
{ "$": "auto_text_key", "code": 1405, "label": "ս" },
{ "$": "auto_text_key", "code": 1407, "label": "տ" },
{ "$": "auto_text_key", "code": 1414, "label": "ֆ" },
{ "$": "auto_text_key", "code": 1391, "label": "կ" },
{ "$": "auto_text_key", "code": 1392, "label": "հ" },
{ "$": "auto_text_key", "code": 1395, "label": "ճ" },
{ "$": "auto_text_key", "code": 1412, "label": "ք" },
{ "$": "auto_text_key", "code": 1388, "label": "լ" },
{ "$": "auto_text_key", "code": 1385, "label": "թ" }
],
[
{ "$": "auto_text_key", "code": 1382, "label": "զ" },
{ "$": "auto_text_key", "code": 1409, "label": "ց" },
{ "$": "auto_text_key", "code": 1379, "label": "գ" },
{ "$": "auto_text_key", "code": 1410, "label": "ւ" },
{ "$": "auto_text_key", "code": 1402, "label": "պ" },
{ "$": "auto_text_key", "code": 1398, "label": "ն" },
{ "$": "auto_text_key", "code": 1396, "label": "մ" },
{ "$": "auto_text_key", "code": 1399, "label": "շ" }
]
]

View File

@@ -0,0 +1,20 @@
[
[
{ "code": -11, "label": "shift", "type": "modifier" },
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "$": "variation_selector",
"default": { "code": 44, "label": ",", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -227, "label": "language_switch", "type": "system_gui" },
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 1417, "label": "։", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -0,0 +1,19 @@
[
[
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": -227, "label": "language_switch", "type": "system_gui" },
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
{ "$": "variation_selector",
"default": { "code": 1548, "label": "،", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": 32, "label": "space" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -0,0 +1,92 @@
[
[
{ "code": 64, "label": "@" },
{ "code": 35, "label": "#", "popup": {
"main": { "code": 8470, "label": "№" }
} },
{ "code": -801, "label": "currency_slot_1", "popup": {
"main": { "code": -802, "label": "currency_slot_2" },
"relevant": [
{ "code": -806, "label": "currency_slot_6" },
{ "code": -803, "label": "currency_slot_3" },
{ "code": -804, "label": "currency_slot_4" },
{ "code": -805, "label": "currency_slot_5" }
]
} },
{ "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": 171, "label": "«", "popup": {
"main": { "code": 34, "label": "\"" },
"relevant": [
{ "code": 8221, "label": "”" },
{ "code": 8222, "label": "„" },
{ "code": 8220, "label": "“" },
{ "code": 8249, "label": "" }
]
} },
{ "code": 187, "label": "»", "popup": {
"main": { "code": 39, "label": "'" },
"relevant": [
{ "code": 8217, "label": "" },
{ "code": 8218, "label": "" },
{ "code": 8216, "label": "" },
{ "code": 8250, "label": "" }
]
} },
{ "code": 1373, "label": "՝", "popup": {
"main": { "code": 58, "label": ":" }
} },
{ "code": 46, "label": "." },
{ "code": 1372, "label": "՜", "popup": {
"main": { "code": 33, "label": "!" }
} },
{ "code": 1374, "label": "՞", "popup": {
"main": { "code": 63, "label": "?" },
"relevant": [
{ "code": 191, "label": "¿" },
{ "code": 8253, "label": "‽" }
]
} },
{ "code": 1371, "label": "՛" }
]
]

View File

@@ -0,0 +1,17 @@
[
[
{ "code": -203, "label": "view_symbols2", "type": "system_gui" },
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],
[
{ "code": -201, "label": "view_characters", "type": "system_gui" },
{ "code": 44, "label": "," },
{ "code": -205, "label": "view_numeric_advanced", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 1417, "label": "։", "popup": {
"main": { "code": 46, "label": "." }
} },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -70,6 +70,10 @@
"id": "fa",
"authors": [ "PHELAT" ]
},
{
"id": "fa2",
"authors": [ "M-Koushan" ]
},
{
"id": "fi",
"authors": [ "patrickgold" ]
@@ -90,6 +94,10 @@
"id": "hu",
"authors": [ "zoli111, gabik65" ]
},
{
"id": "hy",
"authors": [ "PJTSearch" ]
},
{
"id": "is",
"authors": [ "patrickgold" ]
@@ -172,7 +180,11 @@
},
{
"id": "uk",
"authors": [ "williamtheaker", "33kk" ]
"authors": [ "williamtheaker", "33kk", "honsiorovskyi" ]
},
{
"id": "uk-cyr-ext",
"authors": [ "williamtheaker", "33kk", "honsiorovskyi" ]
},
{
"id": "ur-PK",
@@ -423,6 +435,18 @@
"numericRow": "org.florisboard.layouts:persian"
}
},
{
"languageTag": "fa-FA",
"composer": "org.florisboard.composers:appender",
"currencySet": "org.florisboard.currencysets:rial",
"popupMapping": "org.florisboard.localization:fa2",
"preferred": {
"characters": "org.florisboard.layouts:persian2",
"symbols": "org.florisboard.layouts:persian",
"symbols2": "org.florisboard.layouts:persian",
"numericRow": "org.florisboard.layouts:persian"
}
},
{
"languageTag": "ar",
"composer": "org.florisboard.composers:appender",
@@ -435,6 +459,15 @@
"numericRow": "org.florisboard.layouts:eastern_arabic"
}
},
{
"languageTag": "az",
"composer": "org.florisboard.composers:appender",
"currencySet": "org.florisboard.currencysets:azerbaijani_manat",
"popupMapping": "org.florisboard.localization:tr",
"preferred": {
"characters": "org.florisboard.layouts:azerbaijani"
}
},
{
"languageTag": "hu",
"composer": "org.florisboard.composers:appender",
@@ -462,6 +495,26 @@
"characters": "org.florisboard.layouts:qwertz"
}
},
{
"languageTag": "hy",
"composer": "org.florisboard.composers:appender",
"currencySet": "org.florisboard.currencysets:armenian_dram",
"popupMapping": "org.florisboard.localization:hy",
"preferred": {
"characters": "org.florisboard.layouts:western_armenian",
"symbols": "org.florisboard.layouts:armenian"
}
},
{
"languageTag": "hy",
"composer": "org.florisboard.composers:appender",
"currencySet": "org.florisboard.currencysets:armenian_dram",
"popupMapping": "org.florisboard.localization:hy",
"preferred": {
"characters": "org.florisboard.layouts:eastern_armenian",
"symbols": "org.florisboard.layouts:armenian"
}
},
{
"languageTag": "ru",
"composer": "org.florisboard.composers:appender",

View File

@@ -90,7 +90,7 @@
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".ir"},
"main": { "code": -255, "label": ".sa"},
"relevant": [
{ "code": -255, "label": ".gov" },
{ "code": -255, "label": ".edu" },

View File

@@ -8,12 +8,12 @@
]
},
"ا": {
"main": { "code": 1570, "label": "آ"},
"relevant": [
{ "code": 1649, "label": "ٱ" },
{ "code": 1569, "label": "ء" },
{ "code": 1571, "label": "أ" },
{ "code": 1573, "label": "إ" },
{ "code": 1570, "label": "آ" }
{ "code": 1573, "label": "إ" }
]
},
"ه": {

View File

@@ -0,0 +1,99 @@
{
"all": {
"ص": {
"relevant": [
{ "code": 1590, "label": "ض" }
]
},
"half_space": {
"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": "ٔ" }
]
},
"ی": {
"relevant": [
{ "code": 1574, "label": "ئ" },
{ "code": 1610, "label": "ي" },
{ "code": 1746, "label": "ے" }
]
},
"ا": {
"main": { "code": 1570, "label": "آ"},
"relevant": [
{ "code": 1649, "label": "ٱ" },
{ "code": 1569, "label": "ء" },
{ "code": 1571, "label": "أ" },
{ "code": 1573, "label": "إ" }
]
},
"ه": {
"relevant": [
{ "code": 1729, "label": "ہ" },
{ "code": 1728, "label": "ۀ" },
{ "code": 1726, "label": "ھ" }
]
},
"ت": {
"relevant": [
{ "code": 1579, "label": "ث" }
]
},
"ک": {
"relevant": [
{ "code": 1706, "label": "ڪ"}
]
},
"ط": {
"relevant": [
{ "code": 1592, "label": "ظ" }
]
},
"ز": {
"relevant": [
{ "code": 1688, "label": "ژ" }
]
},
"و": {
"relevant": [
{ "code": 1572, "label": "ؤ" }
]
},
"گ": {
"relevant": [
{ "code": 1662, "label": "پ" }
]
},
"~right": {
"main": { "code": 1567, "label": "؟"},
"relevant": [
{ "code": 1563, "label": "؛" },
{ "code": 58, "label": ":"},
{ "code": 33, "label": "!" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".ir"},
"relevant": [
{ "code": -255, "label": ".gov" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".com" },
{ "code": -255, "label": ".net" }
]
}
}
}

View File

@@ -0,0 +1,41 @@
{
"all": {
"ե": {
"main": { "$": "auto_text_key", "code": 1415, "label": "և" }
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
{ "code": 38, "label": "&" },
{ "code": 37, "label": "%" },
{ "code": 43, "label": "+" },
{ "code": 171, "label": "«" },
{ "code": 187, "label": "»" },
{ "code": 45, "label": "-" },
{ "code": 1373, "label": "՝" },
{ "code": 1371, "label": "՛" },
{ "code": 64, "label": "@" },
{ "code": 46, "label": "." },
{ "code": 47, "label": "/" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 35, "label": "#" },
{ "code": 1372, "label": "՜" },
{ "code": 1374, "label": "՞" },
{ "code": 1415, "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" }
]
}
}
}

View File

@@ -0,0 +1,74 @@
{
"all": {
"е": {
"relevant": [
{ "$": "auto_text_key", "code": 1105, "label": "ё" }
]
},
"у": {
"relevant": [
{ "$": "auto_text_key", "code": 1118, "label": "ў" }
]
},
"г": {
"relevant": [
{ "$": "auto_text_key", "code": 1169, "label": "ґ" }
]
},
"і": {
"relevant": [
{ "$": "auto_text_key", "code": 1111, "label": "ї" },
{ "$": "auto_text_key", "code": 1099, "label": "ы" }
]
},
"є": {
"relevant": [
{ "$": "auto_text_key", "code": 8212, "label": "—" },
{ "$": "auto_text_key", "code": 1101, "label": "э" }
]
},
"ь": {
"relevant": [
{ "$": "auto_text_key", "code": 700, "label": "ʼ" },
{ "$": "auto_text_key", "code": 39, "label": "'" },
{ "$": "auto_text_key", "code": 1098, "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": "/" },
{ "$": "layout_direction_selector",
"ltr": { "code": 40, "label": "(" },
"rtl": { "code": 41, "label": "(" }
},
{ "$": "layout_direction_selector",
"ltr": { "code": 41, "label": ")" },
"rtl": { "code": 40, "label": ")" }
},
{ "code": 35, "label": "#" },
{ "code": 33, "label": "!" },
{ "code": 63, "label": "?" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".ua" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]
}
}
}

View File

@@ -1,10 +1,26 @@
{
"all": {
"г": {
"relevant": [
{ "$": "auto_text_key", "code": 1169, "label": "ґ" }
]
},
"і": {
"relevant": [
{ "$": "auto_text_key", "code": 1111, "label": "ї" }
]
},
"є": {
"relevant": [
{ "$": "auto_text_key", "code": 8212, "label": "—" }
]
},
"ь": {
"relevant": [
{ "$": "auto_text_key", "code": 700, "label": "ʼ" },
{ "$": "auto_text_key", "code": 39, "label": "'" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
@@ -37,7 +53,6 @@
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".ua" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]

View File

@@ -183,7 +183,7 @@
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".ir"},
"main": { "code": -255, "label": ".pk"},
"relevant": [
{ "code": -255, "label": ".gov" },
{ "code": -255, "label": ".edu" },

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_black",
"label": "Floris Black",
"authors": [ "serebit" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#388E3C",
"colorPrimaryDark": "#306D32",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#EEEEEE"
},
"keyboard": {
"background": "#000000"
},
"key": {
"background": "#212121",
"backgroundPressed": "#3D3D3D",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#000000",
"foreground": "@window/textColor"
},
"popup": {
"background": "#424242",
"backgroundActive": "#707070",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#7800BF",
"foreground": "@window/textColor"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#20388E3C"}
}
}

View File

@@ -1,77 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_black_borderless",
"label": "Floris Black Borderless",
"authors": [ "serebit" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#388E3C",
"colorPrimaryDark": "#306D32",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#EEEEEE"
},
"keyboard": {
"background": "#000000"
},
"key": {
"background": "transparent",
"backgroundPressed": "#7F616161",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "false"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"key:space": {
"background": "#46616161"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#000000",
"foreground": "@window/textColor"
},
"popup": {
"background": "#363636",
"backgroundActive": "#5F5F5F",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#7800BF",
"foreground": "@window/textColor"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "#212121",
"foreground": "@window/textColor"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#20388E3C"}
}
}

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_day",
"label": "Floris Day",
"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": "#FFFFFF",
"backgroundPressed": "#F5F5F5",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"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": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#E8E8E8",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,78 +0,0 @@
{
"$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"
},
"extractEditLayout": {
"background": "#E8E8E8",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_night",
"label": "Floris Night",
"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": "#424242",
"backgroundPressed": "#616161",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"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": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,78 +0,0 @@
{
"$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"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -17,6 +17,14 @@
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_day_borderless",
"label": "Floris Day (Borderless)",
"authors": [ "patrickgold" ],
"isNight": false,
"isBorderless": true,
"isMaterialYouAware": false
},
{
"id": "floris_night",
"label": "Floris Night",
@@ -24,6 +32,30 @@
"isNight": true,
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_night_borderless",
"label": "Floris Night (Borderless)",
"authors": [ "patrickgold" ],
"isNight": true,
"isBorderless": true,
"isMaterialYouAware": false
},
{
"id": "floris_pure_night",
"label": "Floris Pure Night",
"authors": [ "serebit" ],
"isNight": true,
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_pure_night_borderless",
"label": "Floris Pure Night (Borderless)",
"authors": [ "serebit" ],
"isNight": true,
"isBorderless": true,
"isMaterialYouAware": false
}
]
}

View File

@@ -24,8 +24,8 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
@@ -56,8 +56,8 @@
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
@@ -67,8 +67,8 @@
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shadow-elevation": "2dp",
"shape": "circle()"
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
@@ -127,15 +127,15 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
@@ -152,8 +152,8 @@
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
@@ -162,6 +162,23 @@
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},

View File

@@ -0,0 +1,192 @@
{
"@defines": {
"--primary": "#4caf50",
"--primary-variant": "#388e3c",
"--secondary": "#ff9800",
"--secondary-variant": "#e65100",
"--background": "#e0e0e0",
"--surface": "#f0f0f0",
"--surface-variant": "#ffffff",
"--on-background": "#121212",
"--on-surface": "#000000",
"--on-surface-variant": "#5f5f5f",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#12121248"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},
"one-handed-panel": {
"background": "#e8f5e9",
"foreground": "#424242"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -24,8 +24,8 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
@@ -56,8 +56,8 @@
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
@@ -67,8 +67,8 @@
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shadow-elevation": "2dp",
"shape": "circle()"
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
@@ -127,15 +127,15 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
@@ -152,8 +152,8 @@
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
@@ -162,6 +162,23 @@
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},

View File

@@ -0,0 +1,192 @@
{
"@defines": {
"--primary": "#4caf50",
"--primary-variant": "#388e3c",
"--secondary": "#f57c00",
"--secondary-variant": "#e65100",
"--background": "#212121",
"--surface": "#424242",
"--surface-variant": "#616161",
"--on-background": "#dcdcdc",
"--on-surface": "#ffffff",
"--on-surface-variant": "#a0a0a0",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},
"one-handed-panel": {
"background": "#1b5e20",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -0,0 +1,194 @@
{
"@defines": {
"--primary": "#388e3c",
"--primary-variant": "#306d32",
"--secondary": "#ff9800",
"--secondary-variant": "#804c00",
"--background": "#000000",
"--surface": "#212121",
"--surface-variant": "#3d3d3d",
"--on-background": "#eeeeee",
"--on-surface": "#eeeeee",
"--on-surface-variant": "#ffffff73",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#424242",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#707070",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary-variant)",
"border-width": "1dp"
},
"extracted-landscape-input-action": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary-variant)"
},
"one-handed-panel": {
"background": "#000000",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -0,0 +1,193 @@
{
"@defines": {
"--primary": "#388e3c",
"--primary-variant": "#306d32",
"--secondary": "#ff9800",
"--secondary-variant": "#804c00",
"--background": "#000000",
"--surface": "#212121",
"--surface-variant": "#3d3d3d",
"--on-background": "#eeeeee",
"--on-surface": "#eeeeee",
"--on-surface-variant": "#ffffff73",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "#6161617f",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "#61616146",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#363636",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#5F5F5F",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary-variant)",
"border-width": "1dp"
},
"extracted-landscape-input-action": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary-variant)"
},
"one-handed-panel": {
"background": "#000000",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -19,27 +19,33 @@ package dev.patrickgold.florisboard
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.inputmethodservice.ExtractEditText
import android.os.Build
import android.os.Bundle
import android.util.Size
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.InlineSuggestionsRequest
import android.view.inputmethod.InlineSuggestionsResponse
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.inline.InlinePresentationSpec
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
@@ -49,22 +55,27 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.WindowCompat
import dev.patrickgold.florisboard.app.FlorisAppActivity
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.ProvideLocalizedResources
import dev.patrickgold.florisboard.app.ui.components.FlorisButton
import dev.patrickgold.florisboard.app.ui.components.SystemUiIme
import dev.patrickgold.florisboard.app.ui.devtools.DevtoolsOverlay
import dev.patrickgold.florisboard.common.ViewUtils
import dev.patrickgold.florisboard.common.android.AndroidInternalR
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.isOrientationLandscape
import dev.patrickgold.florisboard.common.android.isOrientationPortrait
@@ -83,6 +94,7 @@ import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.InputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.ProvideKeyboardRowBaseHeight
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
import dev.patrickgold.florisboard.ime.lifecycle.LifecycleInputMethodService
import dev.patrickgold.florisboard.ime.media.MediaInputLayout
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
@@ -92,6 +104,12 @@ import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.snygg.ui.SnyggSurface
import dev.patrickgold.florisboard.snygg.ui.shape
import dev.patrickgold.florisboard.snygg.ui.snyggBackground
import dev.patrickgold.florisboard.snygg.ui.snyggBorder
import dev.patrickgold.florisboard.snygg.ui.snyggShadow
import dev.patrickgold.florisboard.snygg.ui.solidColor
import dev.patrickgold.florisboard.snygg.ui.spSize
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.lang.ref.WeakReference
@@ -107,10 +125,6 @@ private var FlorisImeServiceReference = WeakReference<FlorisImeService?>(null)
/**
* Core class responsible for linking together all managers and UI compose-ables to provide an IME service. Sets
* up the window and context to be lifecycle-aware, so LiveData and Jetpack Compose can be used without issues.
*
* This is a new implementation for the keyboard service class and is replacing the old core class bit by bit.
* The main objective for the new class is to hold as few state as possible and delegate tasks to context-bound
* manager classes.
*/
class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHistoryChangedListener {
companion object {
@@ -212,8 +226,14 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
private var inputViewSize by mutableStateOf(IntSize.Zero)
private val inputFeedbackController by lazy { InputFeedbackController.new(this) }
private var isWindowShown: Boolean = false
private var isFullscreenUiMode by mutableStateOf(false)
private var isExtractUiShown by mutableStateOf(false)
private var resourcesContext by mutableStateOf(this as Context)
init {
setTheme(R.style.FlorisImeTheme)
}
override fun onCreate() {
super.onCreate()
FlorisImeServiceReference = WeakReference(this)
@@ -226,7 +246,7 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
}
override fun onCreateInputView(): View {
super.onCreateInputView()
super.installViewTreeOwners()
val composeView = ComposeInputView()
inputWindowView = composeView
return composeView
@@ -237,6 +257,24 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
return null
}
override fun onCreateExtractTextView(): View {
super.installViewTreeOwners()
// Consider adding a fallback to the default extract edit layout if user reports come
// that this causes a crash, especially if the device manufacturer of the user device
// is a known one to break AOSP standards...
val defaultExtractView = super.onCreateExtractTextView()
if (defaultExtractView == null || defaultExtractView !is ViewGroup) {
return ComposeExtractedLandscapeInputView(null)
}
val extractEditText = defaultExtractView.findViewById<ExtractEditText>(android.R.id.inputExtractEditText)
(extractEditText?.parent as? ViewGroup)?.removeView(extractEditText)
defaultExtractView.apply {
removeAllViews()
addView(ComposeExtractedLandscapeInputView(extractEditText))
}
return defaultExtractView
}
override fun onDestroy() {
super.onDestroy()
FlorisImeServiceReference = WeakReference(null)
@@ -341,8 +379,17 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
isWindowShown = false
}
override fun onEvaluateFullscreenMode(): Boolean {
return when (prefs.keyboard.landscapeInputUiMode.get()) {
LandscapeInputUiMode.DYNAMICALLY_SHOW -> super.onEvaluateFullscreenMode()
LandscapeInputUiMode.NEVER_SHOW -> false
LandscapeInputUiMode.ALWAYS_SHOW -> true
}
}
override fun updateFullscreenMode() {
super.updateFullscreenMode()
isFullscreenUiMode = isFullscreenMode
updateSoftInputWindowLayoutParameters()
}
@@ -351,6 +398,23 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
activeEditorInstance.updateText(token, text)
}
override fun onUpdateExtractingVisibility(ei: EditorInfo?) {
if (ei != null) {
activeState.update(ei)
activeEditorInstance.editorInfo = ei
}
when (prefs.keyboard.landscapeInputUiMode.get()) {
LandscapeInputUiMode.DYNAMICALLY_SHOW -> super.onUpdateExtractingVisibility(ei)
LandscapeInputUiMode.NEVER_SHOW -> isExtractViewShown = false
LandscapeInputUiMode.ALWAYS_SHOW -> isExtractViewShown = true
}
}
override fun setExtractViewShown(shown: Boolean) {
super.setExtractViewShown(shown)
isExtractUiShown = shown
}
@RequiresApi(Build.VERSION_CODES.R)
override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? {
return if (prefs.smartbar.enabled.get() && prefs.suggestion.api30InlineSuggestionsEnabled.get()) {
@@ -420,7 +484,7 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
val w = window?.window ?: return
WindowCompat.setDecorFitsSystemWindows(w, true)
ViewUtils.updateLayoutHeightOf(w, WindowManager.LayoutParams.MATCH_PARENT)
val layoutHeight = if (isFullscreenMode) {
val layoutHeight = if (isFullscreenUiMode) {
WindowManager.LayoutParams.WRAP_CONTENT
} else {
WindowManager.LayoutParams.MATCH_PARENT
@@ -432,15 +496,39 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
ViewUtils.updateLayoutHeightOf(inputWindowView, layoutHeight)
}
override fun getTextForImeAction(imeOptions: Int): String? {
return try {
when (imeOptions and EditorInfo.IME_MASK_ACTION) {
EditorInfo.IME_ACTION_NONE -> null
EditorInfo.IME_ACTION_GO -> resourcesContext.getString(AndroidInternalR.string.ime_action_go)
EditorInfo.IME_ACTION_SEARCH -> resourcesContext.getString(AndroidInternalR.string.ime_action_search)
EditorInfo.IME_ACTION_SEND -> resourcesContext.getString(AndroidInternalR.string.ime_action_send)
EditorInfo.IME_ACTION_NEXT -> resourcesContext.getString(AndroidInternalR.string.ime_action_next)
EditorInfo.IME_ACTION_DONE -> resourcesContext.getString(AndroidInternalR.string.ime_action_done)
EditorInfo.IME_ACTION_PREVIOUS -> resourcesContext.getString(AndroidInternalR.string.ime_action_previous)
else -> resourcesContext.getString(AndroidInternalR.string.ime_action_default)
}
} catch (_: Throwable) {
super.getTextForImeAction(imeOptions)?.toString()
}
}
@Composable
private fun ImeUiWrapper() {
ProvideLocalizedResources(resourcesContext) {
ProvideKeyboardRowBaseHeight {
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
FlorisImeTheme {
// Outer box is necessary as an "outer window"
Box(modifier = Modifier.fillMaxSize()) {
DevtoolsUi()
Column(modifier = Modifier.fillMaxWidth()) {
if (!(isFullscreenUiMode && isExtractUiShown)) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
) {
DevtoolsUi()
}
}
ImeUi()
}
SystemUiIme()
@@ -452,7 +540,7 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun BoxScope.ImeUi() {
private fun ImeUi() {
val activeState by keyboardManager.observeActiveState()
val keyboardStyle = FlorisImeTheme.style.get(
element = FlorisImeUi.Keyboard,
@@ -467,7 +555,6 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.align(Alignment.BottomStart)
.onGloballyPositioned { coords -> inputViewSize = coords.size }
// Do not remove below line or touch input may get stuck
.pointerInteropFilter { false },
@@ -521,23 +608,13 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
}
}
}
}
@Composable
private fun BoxScope.DevtoolsUi() = with(LocalDensity.current) {
private fun DevtoolsUi() {
val devtoolsEnabled by prefs.devtools.enabled.observeAsState()
if (devtoolsEnabled) {
val maxHeight = inputWindowView?.measuredHeight?.let { windowHeight ->
windowHeight - inputViewSize.height - FlorisImeSizing.Static.smartbarHeightPx
} ?: inputViewSize.height
DevtoolsOverlay(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.heightIn(max = maxHeight.toDp())
.align(Alignment.TopStart),
)
DevtoolsOverlay(modifier = Modifier.fillMaxSize())
}
}
@@ -561,4 +638,111 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
updateSoftInputWindowLayoutParameters()
}
}
private inner class ComposeExtractedLandscapeInputView(eet: ExtractEditText?) : FrameLayout(this) {
val composeView: ComposeView
val extractEditText: ExtractEditText
init {
isHapticFeedbackEnabled = true
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
extractEditText = (eet ?: ExtractEditText(context)).also {
it.id = android.R.id.inputExtractEditText
it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
it.background = null
it.gravity = Gravity.TOP
it.isVerticalScrollBarEnabled = true
}
addView(extractEditText)
composeView = ComposeView(context).also { it.setContent { Content() } }
addView(composeView)
}
@Composable
fun Content() {
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
FlorisImeTheme {
val layoutStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputLayout)
val fieldStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputField)
val actionStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputAction)
val activeState by keyboardManager.observeActiveState()
Box(
modifier = Modifier
.snyggBackground(layoutStyle, FlorisImeTheme.fallbackSurfaceColor()),
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
val fieldColor = fieldStyle.foreground.solidColor(FlorisImeTheme.fallbackContentColor())
AndroidView(
modifier = Modifier
.padding(8.dp)
.fillMaxHeight()
.weight(1f)
.snyggShadow(fieldStyle)
.snyggBorder(fieldStyle)
.snyggBackground(fieldStyle),
factory = { extractEditText },
update = { view ->
view.background = null
view.backgroundTintList = null
view.foregroundTintList = null
view.setTextColor(fieldColor.toArgb())
view.setHintTextColor(fieldColor.copy(fieldColor.alpha * 0.6f).toArgb())
view.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
fieldStyle.fontSize.spSize(default = 16.sp).value,
)
},
)
FlorisButton(
onClick = {
val ei = activeEditorInstance.editorInfo
if (ei != null) {
if (ei.actionId != 0) {
activeEditorInstance.inputConnection?.performEditorAction(ei.actionId)
} else {
activeEditorInstance.performEnterAction(activeState.imeOptions.enterAction)
}
}
},
modifier = Modifier.padding(horizontal = 8.dp),
text = activeEditorInstance.editorInfo?.actionLabel?.toString()
?: getTextForImeAction(activeState.imeOptions.enterAction.toInt())
?: "ACTION",
shape = actionStyle.shape.shape(),
colors = ButtonDefaults.buttonColors(
backgroundColor = actionStyle.background.solidColor(FlorisImeTheme.fallbackContentColor()),
contentColor = actionStyle.foreground.solidColor(FlorisImeTheme.fallbackSurfaceColor()),
),
)
}
}
}
}
}
override fun getAccessibilityClassName(): CharSequence {
return javaClass.name
}
override fun onAttachedToWindow() {
removeView(extractEditText)
super.onAttachedToWindow()
try {
(parent as LinearLayout).let { extractEditLayout ->
extractEditLayout.layoutParams = LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
).also { it.setMargins(0, 0, 0, 0) }
extractEditLayout.setPadding(0, 0, 0, 0)
}
} catch (e: Throwable) {
flogError { e.message.toString() }
}
}
}
}

View File

@@ -16,9 +16,14 @@
package dev.patrickgold.florisboard.app.prefs
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalConfiguration
import dev.patrickgold.florisboard.app.AppTheme
import dev.patrickgold.florisboard.app.ui.settings.theme.DisplayColorsAs
import dev.patrickgold.florisboard.app.ui.settings.theme.DisplayKbdAfterDialogs
import dev.patrickgold.florisboard.common.android.isOrientationPortrait
import dev.patrickgold.florisboard.common.observeAsTransformingState
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
@@ -41,6 +46,7 @@ import dev.patrickgold.florisboard.snygg.SnyggLevel
import dev.patrickgold.florisboard.util.VersionName
import dev.patrickgold.jetpref.datastore.JetPref
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
import dev.patrickgold.jetpref.datastore.model.observeAsState
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
@@ -438,6 +444,24 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
mergeHintPopups = mergeHintPopupsEnabled.get(),
)
}
@Composable
fun fontSizeMultiplier(): Float {
val configuration = LocalConfiguration.current
val oneHandedMode by oneHandedMode.observeAsState()
val oneHandedModeFactor by oneHandedModeScaleFactor.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplierBase by if (configuration.isOrientationPortrait()) {
fontSizeMultiplierPortrait
} else {
fontSizeMultiplierLandscape
}.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplier = fontSizeMultiplierBase * if (oneHandedMode != OneHandedMode.OFF && configuration.isOrientationPortrait()) {
oneHandedModeFactor
} else {
1.0f
}
return fontSizeMultiplier
}
}
val localization = Localization()

View File

@@ -41,9 +41,10 @@ private val LocalAppNameString = staticCompositionLocalOf {
@Composable
fun ProvideLocalizedResources(
resourcesContext: Context,
forceLayoutDirection: LayoutDirection? = null,
content: @Composable () -> Unit,
) {
val layoutDirection = when (resourcesContext.resources.configuration.layoutDirection) {
val layoutDirection = forceLayoutDirection ?: when (resourcesContext.resources.configuration.layoutDirection) {
View.LAYOUT_DIRECTION_LTR -> LayoutDirection.Ltr
View.LAYOUT_DIRECTION_RTL -> LayoutDirection.Rtl
else -> error("Given configuration specifies invalid layout direction!")

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 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.app.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
@Composable
@NonRestartableComposable
fun DisposableLifecycleEffect(
lifecycle: Lifecycle = LocalLifecycleOwner.current.lifecycle,
onResume: () -> Unit,
onPause: () -> Unit,
) {
DisposableEffect(lifecycle) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> onResume()
Lifecycle.Event.ON_PAUSE -> onPause()
else -> { }
}
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}

View File

@@ -29,6 +29,7 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
@@ -37,6 +38,7 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.unit.dp
@@ -48,12 +50,14 @@ fun FlorisButton(
icon: Painter? = null,
text: String,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
colors: ButtonColors = ButtonDefaults.buttonColors(),
) {
Button(
modifier = modifier,
enabled = enabled,
shape = shape,
colors = colors,
contentPadding = contentPadding,
onClick = onClick,
@@ -78,12 +82,14 @@ fun FlorisOutlinedButton(
icon: Painter? = null,
text: String,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
) {
OutlinedButton(
modifier = modifier,
enabled = enabled,
shape = shape,
colors = colors,
contentPadding = contentPadding,
onClick = onClick,
@@ -108,12 +114,14 @@ fun FlorisTextButton(
icon: Painter? = null,
text: String,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
colors: ButtonColors = ButtonDefaults.textButtonColors(),
) {
TextButton(
modifier = modifier,
enabled = enabled,
shape = shape,
colors = colors,
contentPadding = contentPadding,
onClick = onClick,

View File

@@ -62,7 +62,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.app.ui.theme.outline
private val StepHeaderPaddingVertical = 16.dp
private val StepHeaderPaddingVertical = 8.dp
private val StepHeaderNumberBoxSize = 40.dp
private val StepHeaderNumberBoxPaddingEnd = 16.dp
private val StepHeaderTextBoxHeight = 32.dp

View File

@@ -23,12 +23,15 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontFamily
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.util.*
@Composable
@@ -39,17 +42,22 @@ fun AndroidLocalesScreen() = FlorisScreen {
val availableLocales = remember { Locale.getAvailableLocales().sortedBy { it.toLanguageTag() } }
content {
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
SelectionContainer(modifier = Modifier.fillMaxWidth()) {
LazyColumn {
items(availableLocales) {
items(availableLocales) { locale ->
Row {
Text(
text = it.toLanguageTag().padEnd(12),
text = locale.toLanguageTag().padEnd(12),
fontFamily = FontFamily.Monospace,
)
Text(
modifier = Modifier.weight(1.0f),
text = it.getDisplayName(it),
text = when (displayLanguageNamesIn) {
DisplayLanguageNamesIn.SYSTEM_LOCALE -> locale.displayName
DisplayLanguageNamesIn.NATIVE_LOCALE -> locale.getDisplayName(locale)
},
fontFamily = FontFamily.Monospace,
)
}

View File

@@ -19,6 +19,7 @@ package dev.patrickgold.florisboard.app.ui.devtools
import android.view.textservice.SuggestionsInfo
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.LocalContentColor
@@ -50,12 +51,8 @@ private val CardBackground = Color.Black.copy(0.6f)
private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.default().base)
@Composable
fun DevtoolsOverlay(
modifier: Modifier = Modifier,
) {
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
val prefs by florisPreferenceModel()
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
@@ -66,11 +63,7 @@ fun DevtoolsOverlay(
) {
Column(modifier = modifier) {
if (showPrimaryClip) {
val primaryClip by clipboardManager.primaryClip.observeAsState()
Text(
text = primaryClip.toString(),
color = Color.White,
)
DevtoolsClipboardOverlay()
}
if (showSpellingOverlay) {
DevtoolsSpellingOverlay()
@@ -79,6 +72,21 @@ fun DevtoolsOverlay(
}
}
@Composable
private fun DevtoolsClipboardOverlay() {
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
DevtoolsOverlayBox(title = "Clipboard overlay") {
val primaryClip by clipboardManager.primaryClip.observeAsState()
Text(
modifier = Modifier.padding(bottom = 8.dp, start = 8.dp, end = 8.dp),
text = primaryClip.toString(),
color = Color.White,
)
}
}
@Composable
private fun DevtoolsSpellingOverlay() {
val context = LocalContext.current
@@ -87,18 +95,8 @@ private fun DevtoolsSpellingOverlay() {
val debugOverlayVersion by spellingManager.debugOverlayVersion.observeAsNonNullState()
val suggestionsInfos = remember(debugOverlayVersion) { spellingManager.debugOverlaySuggestionsInfos.snapshot() }
Column(
modifier = Modifier
.padding(all = 8.dp)
.fillMaxWidth()
.background(CardBackground),
) {
val sortedEntries = suggestionsInfos.entries.sortedByDescending { it.key }
Text(
modifier = Modifier.padding(all = 8.dp),
text = "Spelling overlay (${sortedEntries.size})",
fontSize = 14.sp,
)
val sortedEntries = suggestionsInfos.entries.sortedByDescending { it.key }
DevtoolsOverlayBox(title = "Spelling overlay (${sortedEntries.size})") {
for ((timestamp, wordInfoPair) in sortedEntries) {
val (word, info) = wordInfoPair
val isTypo = (info.suggestionsAttributes and SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0
@@ -131,3 +129,23 @@ private fun DevtoolsSpellingOverlay() {
}
}
}
@Composable
private fun DevtoolsOverlayBox(
title: String,
content: @Composable ColumnScope.() -> Unit,
) {
Column(
modifier = Modifier
.padding(all = 8.dp)
.fillMaxWidth()
.background(CardBackground),
) {
Text(
modifier = Modifier.padding(all = 8.dp),
text = title,
fontSize = 14.sp,
)
content()
}
}

View File

@@ -115,6 +115,7 @@ fun AdvancedScreen() = FlorisScreen {
"tr",
"uk",
"zgh",
"zh-CN",
).map { languageTag ->
if (languageTag == "auto") {
entry(

View File

@@ -52,7 +52,6 @@ import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.common.android.readToFile
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.res.FileRegistry
import dev.patrickgold.florisboard.res.ZipUtils
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.res.ext.ExtensionManager
@@ -65,6 +64,7 @@ import dev.patrickgold.jetpref.datastore.ui.Preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.FileNotFoundException
import java.text.DateFormat
import java.util.*
@@ -84,6 +84,7 @@ fun RestoreScreen() = FlorisScreen {
title = stringRes(R.string.backup_and_restore__restore__title)
previewFieldVisible = false
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val context = LocalContext.current
val cacheManager by context.cacheManager()
@@ -108,7 +109,11 @@ fun RestoreScreen() = FlorisScreen {
workspace.zipFile = workspace.inputDir.subFile(Restore.BACKUP_ARCHIVE_FILE_NAME)
context.contentResolver.readToFile(uri, workspace.zipFile)
ZipUtils.unzip(workspace.zipFile, workspace.outputDir)
workspace.metadata = workspace.outputDir.subFile(Backup.METADATA_JSON_NAME).readJson()
workspace.metadata = try {
workspace.outputDir.subFile(Backup.METADATA_JSON_NAME).readJson()
} catch (e: FileNotFoundException) {
error("Invalid archive: either backup_metadata.json is missing or file is not a ZIP archive.")
}
workspace.restoreWarningId = when {
workspace.metadata.versionCode != BuildConfig.VERSION_CODE -> {
R.string.backup_and_restore__restore__metadata_warn_different_version
@@ -132,7 +137,6 @@ fun RestoreScreen() = FlorisScreen {
)
suspend fun performRestore() {
val prefs by florisPreferenceModel()
val workspace = restoreWorkspace!!
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
if (restoreFilesSelector.jetprefDatastore) {
@@ -228,9 +232,7 @@ fun RestoreScreen() = FlorisScreen {
FlorisOutlinedButton(
onClick = {
runCatching {
restoreDataFromFileSystemLauncher.launch(
FileRegistry.BackupArchive.mediaType
)
restoreDataFromFileSystemLauncher.launch("*/*")
}.onFailure { error ->
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
}

View File

@@ -16,26 +16,51 @@
package dev.patrickgold.florisboard.app.ui.settings.theme
import android.icu.lang.UCharacter
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.flowlayout.FlowRow
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
@@ -45,15 +70,34 @@ import dev.patrickgold.florisboard.app.ui.components.FlorisHyperlinkText
import dev.patrickgold.florisboard.app.ui.components.FlorisIconButton
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedTextField
import dev.patrickgold.florisboard.app.ui.components.florisHorizontalScroll
import dev.patrickgold.florisboard.common.InputMethodUtils
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.showShortToast
import dev.patrickgold.florisboard.common.android.stringRes
import dev.patrickgold.florisboard.common.kotlin.curlyFormat
import dev.patrickgold.florisboard.ime.core.InputKeyEvent
import dev.patrickgold.florisboard.ime.core.InputKeyEventReceiver
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
import dev.patrickgold.florisboard.ime.keyboard.DefaultComputingEvaluator
import dev.patrickgold.florisboard.ime.keyboard.Key
import dev.patrickgold.florisboard.ime.keyboard.Keyboard
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.keyboard.computeIconResId
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
import dev.patrickgold.florisboard.ime.nlp.NATIVE_NULLPTR
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.snygg.SnyggLevel
import dev.patrickgold.florisboard.snygg.SnyggRule
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
private val TransparentTextSelectionColors = TextSelectionColors(
handleColor = Color.Transparent,
backgroundColor = Color.Transparent,
)
internal val SnyggEmptyRuleForAdding = SnyggRule(element = "- select -")
@Composable
@@ -194,13 +238,15 @@ internal fun EditRuleDialog(
)
},
) {
if (codes.isEmpty()) {
Text(
modifier = Modifier.padding(vertical = 4.dp),
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
fontStyle = FontStyle.Italic,
)
}
Text(
modifier = Modifier.padding(vertical = 4.dp),
text = stringRes(if (codes.isEmpty()) {
R.string.settings__theme_editor__no_codes_defined
} else {
R.string.settings__theme_editor__codes_defined
}),
fontStyle = FontStyle.Italic,
)
FlowRow {
for (code in codes) {
FlorisChip(
@@ -247,102 +293,302 @@ internal fun EditRuleDialog(
val initCodeValue = editCodeDialogValue
if (initCodeValue != null) {
var inputCodeString by rememberSaveable(initCodeValue) { mutableStateOf(initCodeValue.toString()) }
var showKeyCodesHelp by rememberSaveable(initCodeValue) { mutableStateOf(false) }
var showError by rememberSaveable(initCodeValue) { mutableStateOf(false) }
var errorId by rememberSaveable(initCodeValue) { mutableStateOf(NATIVE_NULLPTR) }
JetPrefAlertDialog(
title = stringRes(if (initCodeValue == NATIVE_NULLPTR) {
R.string.settings__theme_editor__add_code
} else {
R.string.settings__theme_editor__edit_code
}),
confirmLabel = stringRes(if (initCodeValue == NATIVE_NULLPTR) {
R.string.action__add
} else {
R.string.action__apply
}),
onConfirm = {
val code = inputCodeString.trim().toIntOrNull(radix = 10)
when {
code == null || (code !in KeyCode.Spec.CHARACTERS && code !in KeyCode.Spec.INTERNAL) -> {
errorId = R.string.settings__theme_editor__code_invalid
showError = true
}
code == initCodeValue -> {
editCodeDialogValue = null
}
codes.contains(code) -> {
errorId = R.string.settings__theme_editor__code_already_exists
showError = true
}
else -> {
if (initCodeValue != NATIVE_NULLPTR) {
codes.remove(initCodeValue)
}
codes.add(code)
editCodeDialogValue = null
}
EditCodeValueDialog(
codeValue = initCodeValue,
checkExisting = { codes.contains(it) },
onAdd = { codes.add(it) },
onDelete = { codes.remove(it) },
onDismiss = { editCodeDialogValue = null },
)
}
}
@Composable
private fun EditCodeValueDialog(
codeValue: Int,
checkExisting: (Int) -> Boolean,
onAdd: (Int) -> Unit,
onDelete: (Int) -> Unit,
onDismiss: () -> Unit,
) {
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
var inputCodeString by rememberSaveable(codeValue) {
val str = if (codeValue == 0) "" else codeValue.toString()
mutableStateOf(str)
}
val textKeyData = remember(inputCodeString) {
inputCodeString.toIntOrNull()?.let { code ->
TextKeyData.getCodeInfoAsTextKeyData(code)
}
}
var showKeyCodesHelp by rememberSaveable(codeValue) { mutableStateOf(false) }
var showError by rememberSaveable(codeValue) { mutableStateOf(false) }
var errorId by rememberSaveable(codeValue) { mutableStateOf(NATIVE_NULLPTR) }
val focusRequester = remember { FocusRequester() }
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
var isRecordingKey by remember { mutableStateOf(false) }
var lastRecordingToast by remember { mutableStateOf<Toast?>(null) }
val recordingKeyColor = if (isRecordingKey) {
rememberInfiniteTransition().animateColor(
initialValue = LocalContentColor.current,
targetValue = MaterialTheme.colors.error,
animationSpec = infiniteRepeatable(
tween(750),
repeatMode = RepeatMode.Reverse,
),
).value
} else {
LocalContentColor.current
}
fun requestStartRecording() {
if (isRecordingKey) {
isRecordingKey = false
return
}
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToast(
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
"app_name" to context.stringRes(R.string.floris_app_name),
)
InputMethodUtils.showImePicker(context)
return
}
showError = false
isRecordingKey = true
}
if (isRecordingKey) {
DisposableEffect(Unit) {
val receiver = object : InputKeyEventReceiver {
override fun onInputKeyDown(ev: InputKeyEvent) = Unit
override fun onInputKeyUp(ev: InputKeyEvent) {
inputCodeString = ev.data.code.toString()
isRecordingKey = false
}
},
dismissLabel = stringRes(R.string.action__cancel),
onDismiss = {
editCodeDialogValue = null
},
neutralLabel = if (initCodeValue != NATIVE_NULLPTR) {
stringRes(R.string.action__delete)
} else {
null
},
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
onNeutral = {
codes.remove(initCodeValue)
editCodeDialogValue = null
},
trailingIconTitle = {
FlorisIconButton(
onClick = { showKeyCodesHelp = !showKeyCodesHelp },
modifier = Modifier.offset(x = 12.dp),
icon = painterResource(R.drawable.ic_help_outline),
)
},
) {
Column {
AnimatedVisibility(visible = showKeyCodesHelp) {
Column(modifier = Modifier.padding(bottom = 16.dp)) {
Text(text = stringRes(R.string.settings__theme_editor__code_help_text))
FlorisHyperlinkText(
text = "Characters (unicode-table.com)",
url = stringRes(R.string.florisboard__character_key_codes_url),
)
FlorisHyperlinkText(
text = "Internal (github.com)",
url = stringRes(R.string.florisboard__internal_key_codes_url),
)
}
override fun onInputKeyRepeat(ev: InputKeyEvent) = Unit
override fun onInputKeyCancel(ev: InputKeyEvent) = Unit
}
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
focusRequester.requestFocus()
onDispose {
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
}
}
}
JetPrefAlertDialog(
title = stringRes(if (codeValue == NATIVE_NULLPTR) {
R.string.settings__theme_editor__add_code
} else {
R.string.settings__theme_editor__edit_code
}),
confirmLabel = stringRes(if (codeValue == NATIVE_NULLPTR) {
R.string.action__add
} else {
R.string.action__apply
}),
onConfirm = {
val code = inputCodeString.trim().toIntOrNull(radix = 10)
when {
code == null || (code !in KeyCode.Spec.CHARACTERS && code !in KeyCode.Spec.INTERNAL) -> {
errorId = R.string.settings__theme_editor__code_invalid
showError = true
}
FlorisOutlinedTextField(
value = inputCodeString,
onValueChange = { v ->
inputCodeString = v
showError = false
},
isError = showError,
singleLine = true,
)
AnimatedVisibility(visible = showError) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = stringRes(errorId).curlyFormat(
"c_min" to KeyCode.Spec.CHARACTERS_MIN,
"c_max" to KeyCode.Spec.CHARACTERS_MAX,
"i_min" to KeyCode.Spec.INTERNAL_MIN,
"i_max" to KeyCode.Spec.INTERNAL_MAX,
),
color = MaterialTheme.colors.error,
code == codeValue -> {
onDismiss()
}
checkExisting(code) -> {
errorId = R.string.settings__theme_editor__code_already_exists
showError = true
}
else -> {
if (codeValue != NATIVE_NULLPTR) {
onDelete(codeValue)
}
onAdd(code)
onDismiss()
}
}
},
dismissLabel = stringRes(R.string.action__cancel),
onDismiss = onDismiss,
neutralLabel = if (codeValue != NATIVE_NULLPTR) {
stringRes(R.string.action__delete)
} else {
null
},
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
onNeutral = {
onDelete(codeValue)
onDismiss()
},
trailingIconTitle = {
FlorisIconButton(
onClick = { showKeyCodesHelp = !showKeyCodesHelp },
modifier = Modifier.offset(x = 12.dp),
icon = painterResource(R.drawable.ic_help_outline),
)
},
) {
Column {
AnimatedVisibility(visible = showKeyCodesHelp) {
Column(modifier = Modifier.padding(bottom = 16.dp)) {
Text(text = stringRes(R.string.settings__theme_editor__code_recording_help_text))
Text(text = stringRes(R.string.settings__theme_editor__code_help_text))
FlorisHyperlinkText(
text = "Characters (unicode-table.com)",
url = stringRes(R.string.florisboard__character_key_codes_url),
)
FlorisHyperlinkText(
text = "Internal (github.com)",
url = stringRes(R.string.florisboard__internal_key_codes_url),
)
}
}
TextKeyDataPreviewBox(
modifier = Modifier.padding(bottom = 8.dp),
textKeyData = textKeyData,
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
val textSelectionColors = if (isRecordingKey) {
TransparentTextSelectionColors
} else {
LocalTextSelectionColors.current
}
CompositionLocalProvider(LocalTextSelectionColors provides textSelectionColors) {
FlorisOutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester)
.weight(1f),
value = inputCodeString,
onValueChange = { v ->
inputCodeString = v
showError = false
},
placeholder = when {
isRecordingKey -> {
stringRes(R.string.settings__theme_editor__code_recording_placeholder)
}
inputCodeString.isEmpty() -> {
stringRes(R.string.settings__theme_editor__code_placeholder)
}
else -> {
null
}
},
isError = showError,
singleLine = true,
colors = if (isRecordingKey) {
TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Transparent,
cursorColor = Color.Transparent,
)
} else {
TextFieldDefaults.outlinedTextFieldColors()
},
)
}
FlorisIconButton(
onClick = { requestStartRecording() },
icon = painterResource(R.drawable.ic_pageview),
iconColor = recordingKeyColor,
)
}
AnimatedVisibility(visible = showError) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = stringRes(errorId).curlyFormat(
"c_min" to KeyCode.Spec.CHARACTERS_MIN,
"c_max" to KeyCode.Spec.CHARACTERS_MAX,
"i_min" to KeyCode.Spec.INTERNAL_MIN,
"i_max" to KeyCode.Spec.INTERNAL_MAX,
),
color = MaterialTheme.colors.error,
)
}
}
}
}
@Composable
private fun TextKeyDataPreviewBox(
textKeyData: TextKeyData?,
modifier: Modifier = Modifier,
) {
val data = textKeyData ?: TextKeyData.UNSPECIFIED
val context = LocalContext.current
val evaluator = remember(context) {
object : ComputingEvaluator by DefaultComputingEvaluator {
val keyboard = object : Keyboard() {
override val mode = KeyboardMode.NUMERIC_ADVANCED
override fun getKeyForPos(pointerX: Float, pointerY: Float) = error("not implemented")
override fun keys() = error("not implemented")
override fun layout(keyboardWidth: Float, keyboardHeight: Float, desiredKey: Key) =
error("not implemented")
}
override fun context() = context
override fun keyboard() = keyboard
}
}
val label = remember(data) { evaluator.computeLabel(data) }
val iconId = remember(data) { evaluator.computeIconResId(data) }
val displayName = remember(data) {
if (data.code > 0 && AndroidVersion.ATLEAST_API24_N) {
UCharacter.getName(data.code) ?: UCharacter.getExtendedName(data.code)
} else {
data.label
}
}
Row(modifier = modifier) {
Box(
modifier = Modifier
.padding(end = 16.dp)
.background(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
shape = MaterialTheme.shapes.medium,
)
.height(36.dp)
.widthIn(min = 36.dp)
.align(Alignment.CenterVertically),
contentAlignment = Alignment.Center,
) {
if (label != null) {
Text(
text = label,
fontSize = 16.sp,
maxLines = 1,
softWrap = false,
)
}
if (iconId != null) {
Icon(
modifier = Modifier.requiredSize(24.dp),
painter = painterResource(iconId),
contentDescription = null,
)
}
}
Column(modifier = Modifier.weight(1f)) {
Text(text = displayName)
Text(text = data.type.toString())
}
}
}

View File

@@ -215,7 +215,11 @@ class FlorisLocale private constructor(val base: Locale) {
*
* @see java.util.Locale.getDisplayLanguage
*/
fun displayLanguage(locale: FlorisLocale = default()): String = base.getDisplayLanguage(locale.base)
fun displayLanguage(locale: FlorisLocale = default()): String {
return base.getDisplayLanguage(locale.base).replaceFirstChar {
if (it.isLowerCase()) it.titlecase(locale.base) else it.toString()
}
}
/**
* Returns the name of this locale's country, localized to [locale].

View File

@@ -1,40 +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.common
import android.content.Context
import android.util.AttributeSet
import android.widget.ViewFlipper
/**
* Custom ViewFlipper class used to prevent an unnecessary exception to be thrown when it is
* detached from a window.
*
* Based on the solution of this SO answer: https://stackoverflow.com/a/8208874/6801193
*/
class FlorisViewFlipper : ViewFlipper {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
override fun onDetachedFromWindow() {
try {
super.onDetachedFromWindow()
} catch (e: IllegalArgumentException) {
stopFlipping()
}
}
}

View File

@@ -33,7 +33,7 @@ private const val IME_SERVICE_CLASS_NAME = "dev.patrickgold.florisboard.FlorisIm
object InputMethodUtils {
@Composable
fun observeIsFlorisboardEnabled(
context: Context = LocalContext.current,
context: Context = LocalContext.current.applicationContext,
foregroundOnly: Boolean = false,
) = AndroidSettings.Secure.observeAsState(
key = Settings.Secure.ENABLED_INPUT_METHODS,
@@ -43,7 +43,7 @@ object InputMethodUtils {
@Composable
fun observeIsFlorisboardSelected(
context: Context = LocalContext.current,
context: Context = LocalContext.current.applicationContext,
foregroundOnly: Boolean = false,
) = AndroidSettings.Secure.observeAsState(
key = Settings.Secure.DEFAULT_INPUT_METHOD,

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) 2021 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.common
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class RegexSerializer : KSerializer<Regex> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ThemeValue", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Regex) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Regex {
return decoder.decodeString().toRegex()
}
}

View File

@@ -16,6 +16,10 @@
package dev.patrickgold.florisboard.common
import android.icu.lang.UCharacter
import android.icu.lang.UCharacterCategory
import dev.patrickgold.florisboard.common.android.AndroidVersion
/**
* Character codes and comments source:
* https://www.w3.org/International/questions/qa-bidi-unicode-controls#basedirection
@@ -40,3 +44,30 @@ object UnicodeCtrlChar {
fun String.stripUnicodeCtrlChars(): String {
return this.replace(UnicodeCtrlChar.Matcher, "")
}
object Unicode {
fun isNonSpacingMark(code: Int): Boolean {
return if (AndroidVersion.ATLEAST_API24_N) {
UCharacter.getType(code).toByte() == UCharacterCategory.NON_SPACING_MARK
} else {
// See: https://en.wikipedia.org/wiki/Combining_character
// https://unicode-table.com/en/blocks/arabic/
return when (code) {
in 0x0300..0x036F, // Combining Diacritical Marks
in 0x1AB0..0x1AFF, // Combining Diacritical Marks Extended
in 0x1DC0..0x1DFF, // Combining Diacritical Marks Supplement
in 0x20D0..0x20FF, // Combining Diacritical Marks for Symbols
in 0xFE20..0xFE2F, // Combining Half Marks
0x0E31, // Thai
in 0x0E34..0x0E3A, // Thai
in 0x0E47..0x0E4E, // Thai
in 0x0610..0x0614, // Honorifics
in 0x064B..0x065F, // Tashkil, Combining maddah, hamza and other
0x0670, // Tashkil (single char)
in 0x06D6..0x06ED, // Quranic annotation signs
-> true
else -> false
}
}
}
}

View File

@@ -20,12 +20,9 @@ package dev.patrickgold.florisboard.common
import android.content.res.Resources
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.core.view.children
import androidx.core.view.isVisible
/**
* This file has been taken from the Android LatinIME project. Following modifications were done to
@@ -101,22 +98,4 @@ object ViewUtils {
fun px2dp(px: Float): Float {
return px / (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
}
fun setEnabled(view: View, v: Boolean) {
view.isEnabled = v
if (view is ViewGroup) {
for (childView in view.children) {
setEnabled(childView, v)
}
}
}
fun setVisible(view: View, v: Boolean) {
view.isVisible = v
if (view is ViewGroup) {
for (childView in view.children) {
setVisible(childView, v)
}
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2022 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.common.android
import android.content.res.Resources
/**
* Helper class for retrieving `com.android.internal.R.*` resources.
*
* Usage of this ids should always be done within a try..catch block, as there may be devices which have completely
* modified system resources or something has changed in a newer Android version.
*/
object AndroidInternalR {
@Suppress("ClassName")
object string {
val ime_action_go by lazy {
Resources.getSystem().getIdentifier("ime_action_go", "string", "android")
}
val ime_action_search by lazy {
Resources.getSystem().getIdentifier("ime_action_search", "string", "android")
}
val ime_action_send by lazy {
Resources.getSystem().getIdentifier("ime_action_send", "string", "android")
}
val ime_action_next by lazy {
Resources.getSystem().getIdentifier("ime_action_next", "string", "android")
}
val ime_action_done by lazy {
Resources.getSystem().getIdentifier("ime_action_done", "string", "android")
}
val ime_action_previous by lazy {
Resources.getSystem().getIdentifier("ime_action_previous", "string", "android")
}
val ime_action_default by lazy {
Resources.getSystem().getIdentifier("ime_action_default", "string", "android")
}
}
}

View File

@@ -85,7 +85,7 @@ abstract class AndroidSettingsHelper(
transform: (String?) -> R,
): State<R> {
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
val context = LocalContext.current.applicationContext
val state = remember(key) { mutableStateOf(transform(getString(context, key))) }
DisposableEffect(lifecycleOwner.lifecycle) {
val observer = SystemSettingsObserver(context) {

View File

@@ -29,6 +29,10 @@ class SystemSettingsObserver(
private val listener: OnSystemSettingsChangedListener,
) : ContentObserver(Handler(context.mainLooper)) {
override fun deliverSelfNotifications(): Boolean {
return true
}
override fun onChange(selfChange: Boolean) {
listener.onChanged()
}

View File

@@ -26,8 +26,8 @@ import dev.patrickgold.florisboard.common.kotlin.CurlyArg
*
* @param text The text to show in the toast popup.
*/
fun Context.showShortToast(text: String) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
fun Context.showShortToast(text: String): Toast {
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
}
/**
@@ -35,9 +35,9 @@ fun Context.showShortToast(text: String) {
*
* @param id The string resource id of the text to display. Must not be 0.
*/
fun Context.showShortToast(@StringRes id: Int) {
fun Context.showShortToast(@StringRes id: Int): Toast {
val text = this.stringRes(id)
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
}
/**
@@ -47,9 +47,9 @@ fun Context.showShortToast(@StringRes id: Int) {
* @param id The string resource id of the text to display. Must not be 0.
* @param args The curly arguments which will be filled into the string template identified by [id].
*/
fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg) {
fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
val text = this.stringRes(id, *args)
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
}
/**
@@ -57,8 +57,8 @@ fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg) {
*
* @param text The text to show in the toast popup.
*/
fun Context.showLongToast(text: String) {
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
fun Context.showLongToast(text: String): Toast {
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
}
/**
@@ -66,9 +66,9 @@ fun Context.showLongToast(text: String) {
*
* @param id The string resource id of the text to display. Must not be 0.
*/
fun Context.showLongToast(@StringRes id: Int) {
fun Context.showLongToast(@StringRes id: Int): Toast {
val text = this.stringRes(id)
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
}
/**
@@ -78,7 +78,7 @@ fun Context.showLongToast(@StringRes id: Int) {
* @param id The string resource id of the text to display. Must not be 0.
* @param args The curly arguments which will be filled into the string template identified by [id].
*/
fun Context.showLongToast(@StringRes id: Int, vararg args: CurlyArg) {
fun Context.showLongToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
val text = this.stringRes(id, *args)
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 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.common.kotlin
import kotlinx.coroutines.sync.Mutex
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
class GuardedByLock<out T : Any>(@PublishedApi internal val wrapped: T) {
@PublishedApi
internal val lock = Mutex(locked = false)
suspend inline fun <R> withLock(owner: Any? = null, action: (T) -> R): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
}
lock.lock(owner)
try {
return action(wrapped)
} finally {
lock.unlock(owner)
}
}
}
inline fun <T : Any> guardedByLock(initializer: () -> T): GuardedByLock<T> {
contract {
callsInPlace(initializer, InvocationKind.EXACTLY_ONCE)
}
return GuardedByLock(initializer())
}

View File

@@ -18,10 +18,15 @@ package dev.patrickgold.florisboard.ime.clipboard
import android.content.ContentUris
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import android.media.ThumbnailUtils
import android.provider.MediaStore
import android.util.Size
import androidx.annotation.DrawableRes
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -38,6 +43,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
@@ -61,6 +67,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
@@ -75,6 +82,7 @@ import dev.patrickgold.florisboard.app.ui.components.safeTimes
import dev.patrickgold.florisboard.app.ui.theme.Green500
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.common.android.AndroidKeyguardManager
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.showShortToast
import dev.patrickgold.florisboard.common.android.systemService
import dev.patrickgold.florisboard.common.observeAsNonNullState
@@ -83,7 +91,6 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
@@ -97,6 +104,7 @@ import dev.patrickgold.florisboard.snygg.ui.solidColor
import dev.patrickgold.florisboard.snygg.ui.spSize
import dev.patrickgold.jetpref.datastore.model.observeAsState
private val ContentPadding = PaddingValues(horizontal = 4.dp)
private val ItemMargin = PaddingValues(all = 6.dp)
private val ItemPadding = PaddingValues(vertical = 8.dp, horizontal = 12.dp)
@@ -118,18 +126,7 @@ fun ClipboardInputLayout(
val historyEnabled by prefs.clipboard.historyEnabled.observeAsState()
val history by clipboardManager.history.observeAsNonNullState()
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
val secondaryRowEnabled by prefs.smartbar.secondaryActionsEnabled.observeAsState()
val secondaryRowExpanded by prefs.smartbar.secondaryActionsExpanded.observeAsState()
val secondaryRowPlacement by prefs.smartbar.secondaryActionsPlacement.observeAsState()
val innerHeight =
if (smartbarEnabled && secondaryRowEnabled && secondaryRowExpanded &&
secondaryRowPlacement != SecondaryRowPlacement.OVERLAY_APP_UI
) {
FlorisImeSizing.smartbarHeight
} else {
0.dp
} + (FlorisImeSizing.keyboardRowBaseHeight * 4)
val innerHeight = FlorisImeSizing.keyboardUiHeight() - FlorisImeSizing.smartbarHeight
var popupItem by remember(history) { mutableStateOf<ClipboardItem?>(null) }
var showClearAllHistory by remember { mutableStateOf(false) }
@@ -194,6 +191,7 @@ fun ClipboardInputLayout(
fun ClipItemView(
item: ClipboardItem,
style: SnyggPropertySet,
contentScrollInsteadOfClip: Boolean,
modifier: Modifier = Modifier,
) {
SnyggSurface(
@@ -243,15 +241,65 @@ fun ClipboardInputLayout(
fontSize = style.fontSize.spSize(),
)
}
} else if (item.type == ItemType.VIDEO) {
val id = ContentUris.parseId(item.uri!!)
val file = ClipboardFileStorage.getFileForId(context, id)
val bitmap = remember(id) {
runCatching {
check(file.exists()) { "Unable to resolve video at ${file.absolutePath}" }
val rawBitmap = if (AndroidVersion.ATLEAST_API29_Q) {
val dataRetriever = MediaMetadataRetriever()
dataRetriever.setDataSource(file.absolutePath)
val width = dataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
val height = dataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
ThumbnailUtils.createVideoThumbnail(file, Size(width!!.toInt(), height!!.toInt()), null)
} else {
@Suppress("DEPRECATION")
ThumbnailUtils.createVideoThumbnail(file.absolutePath, MediaStore.Video.Thumbnails.MINI_KIND)
}
checkNotNull(rawBitmap) { "Unable to decode video at ${file.absolutePath}" }
rawBitmap.asImageBitmap()
}
}
if (bitmap.isSuccess) {
Image(
modifier = Modifier.fillMaxWidth(),
bitmap = bitmap.getOrThrow(),
contentDescription = null,
contentScale = ContentScale.FillWidth,
)
Icon(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 4.dp, bottom = 4.dp)
.background(Color.White, CircleShape),
painter = painterResource(R.drawable.ic_videocam),
contentDescription = null,
tint = Color.Black,
)
} else {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(ItemPadding),
text = bitmap.exceptionOrNull()?.message ?: "Unknown error",
style = TextStyle(textDirection = TextDirection.Ltr),
color = Color.Red,
fontSize = style.fontSize.spSize(),
)
}
} else {
Text(
modifier = Modifier
.fillMaxWidth()
.run { if (contentScrollInsteadOfClip) this.florisVerticalScroll() else this }
.padding(ItemPadding),
text = item.stringRepresentation(),
style = TextStyle(textDirection = TextDirection.ContentOrLtr),
color = style.foreground.solidColor(),
fontSize = style.fontSize.spSize(),
maxLines = if (contentScrollInsteadOfClip) Int.MAX_VALUE else 5,
overflow = TextOverflow.Ellipsis,
)
}
}
@@ -279,7 +327,7 @@ fun ClipboardInputLayout(
)
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
for (item in history.pinned) {
ClipItemView(item, itemStyle)
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
}
}
}
@@ -290,7 +338,7 @@ fun ClipboardInputLayout(
)
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
for (item in history.recent) {
ClipItemView(item, itemStyle)
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
}
}
}
@@ -301,7 +349,7 @@ fun ClipboardInputLayout(
)
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
for (item in history.other) {
ClipItemView(item, itemStyle)
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
}
}
}
@@ -321,6 +369,7 @@ fun ClipboardInputLayout(
modifier = Modifier.widthIn(max = ItemWidth),
item = popupItem!!,
style = itemStyle,
contentScrollInsteadOfClip = true,
)
Column(
modifier = Modifier

View File

@@ -34,7 +34,8 @@ private const val CLIPBOARD_FILES_TABLE = "clipboard_files"
enum class ItemType(val value: Int) {
TEXT(1),
IMAGE(2);
IMAGE(2),
VIDEO(3);
companion object {
fun fromInt(value : Int) : ItemType {
@@ -85,20 +86,25 @@ data class ClipboardItem(
* Returns a new ClipboardItem based on a ClipData.
*
* @param data The ClipData to clone.
* @param cloneUri Whether to store the image using [ClipboardImagesProvider].
* @param cloneUri Whether to store the image using [ClipboardMediaProvider].
*/
fun fromClipData(context: Context, data: ClipData, cloneUri: Boolean) : ClipboardItem {
val dataItem = data.getItemAt(0)
val type = when {
dataItem?.uri != null && data.description.hasMimeType("image/*") -> ItemType.IMAGE
dataItem?.uri != null && data.description.hasMimeType("video/*") -> ItemType.VIDEO
else -> ItemType.TEXT
}
val uri = if (type == ItemType.IMAGE) {
if (dataItem.uri.authority == ClipboardImagesProvider.AUTHORITY || !cloneUri) {
val uri = if (type == ItemType.IMAGE || type == ItemType.VIDEO) {
if (dataItem.uri.authority == ClipboardMediaProvider.AUTHORITY || !cloneUri) {
dataItem.uri
} else {
var displayName = "Image"
var displayName = when (type) {
ItemType.IMAGE -> "Image"
ItemType.VIDEO -> "Video"
else -> "Unknown"
}
tryOrNull {
context.contentResolver.query(dataItem.uri, MEDIA_PROJECTION)?.use { cursor ->
val displayNameColumn = cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
@@ -109,17 +115,23 @@ data class ClipboardItem(
}
val values = ContentValues(3).apply {
put(OpenableColumns.DISPLAY_NAME, displayName)
put(ClipboardImagesProvider.Columns.ImageUri, dataItem.uri.toString())
put(ClipboardImagesProvider.Columns.MimeTypes, data.description.filterMimeTypes("*/*").joinToString(","))
put(ClipboardMediaProvider.Columns.MediaUri, dataItem.uri.toString())
put(ClipboardMediaProvider.Columns.MimeTypes, data.description.filterMimeTypes("*/*").joinToString(","))
}
context.contentResolver.insert(ClipboardImagesProvider.IMAGE_CLIPS_URI, values)
context.contentResolver.insert(when (type) {
ItemType.IMAGE -> ClipboardMediaProvider.IMAGE_CLIPS_URI
ItemType.VIDEO -> ClipboardMediaProvider.VIDEO_CLIPS_URI
else -> error("Impossible.")
}, values)
}
} else { null }
val text = dataItem.coerceToText(context).toString()
val text = dataItem.text?.toString()
val mimeTypes = when (type) {
ItemType.IMAGE -> Array(data.description.mimeTypeCount) { data.description.getMimeType(it) }
ItemType.TEXT -> TEXT_PLAIN
ItemType.IMAGE, ItemType.VIDEO -> {
Array(data.description.mimeTypeCount) { data.description.getMimeType(it) }
}
}
return ClipboardItem(0, type, text, uri, System.currentTimeMillis(), false, mimeTypes)
@@ -130,7 +142,7 @@ data class ClipboardItem(
if (other == null) return false
return when (type) {
ItemType.TEXT -> text == other.getItemAt(0).text
ItemType.IMAGE -> uri == other.getItemAt(0).uri
ItemType.IMAGE, ItemType.VIDEO -> uri == other.getItemAt(0).uri
}
}
@@ -139,12 +151,12 @@ data class ClipboardItem(
*/
fun toClipData(context: Context): ClipData {
return when (type) {
ItemType.IMAGE -> {
ClipData.newUri(context.contentResolver, FLORIS_CLIP_LABEL, uri)
}
ItemType.TEXT -> {
ClipData.newPlainText(FLORIS_CLIP_LABEL, text)
}
ItemType.IMAGE, ItemType.VIDEO -> {
ClipData.newUri(context.contentResolver, FLORIS_CLIP_LABEL, uri)
}
}
}

View File

@@ -25,7 +25,7 @@ import dev.patrickgold.florisboard.res.io.FsFile
import dev.patrickgold.florisboard.res.io.subFile
/**
* Backend helper object which is used by [ClipboardImagesProvider] to serve content.
* Backend helper object which is used by [ClipboardMediaProvider] to serve content.
*/
object ClipboardFileStorage {
private val Context.clipboardFilesDir: FsFile

View File

@@ -30,12 +30,12 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
/**
* Allows apps to access images on the clipboard.
* Allows apps to access images and videos on the clipboard.
*
* This is sometimes called by the UI thread, so all functions are non blocking.
* Database accesses are performed async.
*/
class ClipboardImagesProvider : ContentProvider() {
class ClipboardMediaProvider : ContentProvider() {
private var clipboardFilesDao: ClipboardFilesDao? = null
private val cachedFileInfos: HashMap<Long, ClipboardFileInfo> = hashMapOf()
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@@ -43,18 +43,23 @@ class ClipboardImagesProvider : ContentProvider() {
companion object {
const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.clipboard"
val IMAGE_CLIPS_URI: Uri = Uri.parse("content://$AUTHORITY/clips/images")
val VIDEO_CLIPS_URI: Uri = Uri.parse("content://$AUTHORITY/clips/videos")
private const val IMAGE_CLIP_ITEM = 0
private const val IMAGE_CLIPS_TABLE = 1
private const val VIDEO_CLIP_ITEM = 2
private const val VIDEO_CLIPS_TABLE = 3
val matcher = UriMatcher(UriMatcher.NO_MATCH).apply {
private val Matcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "clips/images/#", IMAGE_CLIP_ITEM)
addURI(AUTHORITY, "clips/images", IMAGE_CLIPS_TABLE)
addURI(AUTHORITY, "clips/videos/#", VIDEO_CLIP_ITEM)
addURI(AUTHORITY, "clips/videos", VIDEO_CLIPS_TABLE)
}
}
object Columns {
const val ImageUri = "image_uri"
const val MediaUri = "media_uri"
const val MimeTypes = "mime_types"
}
@@ -85,16 +90,21 @@ class ClipboardImagesProvider : ContentProvider() {
}
override fun getType(uri: Uri): String? {
return when (matcher.match(uri)) {
IMAGE_CLIP_ITEM -> cachedFileInfos.getOrDefault(ContentUris.parseId(uri), null)?.mimeTypes?.getOrNull(0)
return when (Matcher.match(uri)) {
IMAGE_CLIP_ITEM, VIDEO_CLIP_ITEM -> {
cachedFileInfos.getOrDefault(ContentUris.parseId(uri), null)?.mimeTypes?.getOrNull(0)
}
IMAGE_CLIPS_TABLE -> "${ContentResolver.CURSOR_DIR_BASE_TYPE}/vnd.florisboard.image_clip_table"
VIDEO_CLIPS_TABLE -> "${ContentResolver.CURSOR_DIR_BASE_TYPE}/vnd.florisboard.video_clip_table"
else -> null
}
}
override fun getStreamTypes(uri: Uri, mimeTypeFilter: String): Array<String>? {
return when (matcher.match(uri)) {
IMAGE_CLIP_ITEM -> cachedFileInfos.getOrDefault(ContentUris.parseId(uri), null)?.mimeTypes
return when (Matcher.match(uri)) {
IMAGE_CLIP_ITEM, VIDEO_CLIP_ITEM -> {
cachedFileInfos.getOrDefault(ContentUris.parseId(uri), null)?.mimeTypes
}
else -> null
}
}
@@ -108,12 +118,12 @@ class ClipboardImagesProvider : ContentProvider() {
}
override fun insert(uri: Uri, values: ContentValues?): Uri {
when (matcher.match(uri)) {
IMAGE_CLIPS_TABLE -> {
when (val m = Matcher.match(uri)) {
IMAGE_CLIPS_TABLE, VIDEO_CLIPS_TABLE -> {
return try {
values as ContentValues
val imageUri = Uri.parse(values.getAsString(Columns.ImageUri))
val id = ClipboardFileStorage.cloneUri(context!!, imageUri)
val mediaUri = Uri.parse(values.getAsString(Columns.MediaUri))
val id = ClipboardFileStorage.cloneUri(context!!, mediaUri)
val size = ClipboardFileStorage.getFileForId(context!!, id).length()
val mimeTypes = values.getAsString(Columns.MimeTypes).split(",").toTypedArray()
val displayName = values.getAsString(OpenableColumns.DISPLAY_NAME)
@@ -122,7 +132,11 @@ class ClipboardImagesProvider : ContentProvider() {
ioScope.launch {
clipboardFilesDao?.insert(fileInfo)
}
ContentUris.withAppendedId(IMAGE_CLIPS_URI, id)
if (m == IMAGE_CLIPS_TABLE) {
ContentUris.withAppendedId(IMAGE_CLIPS_URI, id)
} else {
ContentUris.withAppendedId(VIDEO_CLIPS_URI, id)
}
} catch (e: Exception) {
flogError { e.message.toString() }
uri.buildUpon().appendPath("0").build()
@@ -133,8 +147,8 @@ class ClipboardImagesProvider : ContentProvider() {
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
when (matcher.match(uri)) {
IMAGE_CLIP_ITEM -> {
when (Matcher.match(uri)) {
IMAGE_CLIP_ITEM, VIDEO_CLIP_ITEM -> {
val id = ContentUris.parseId(uri)
ClipboardFileStorage.deleteById(context!!, id)
cachedFileInfos.remove(id)

View File

@@ -29,6 +29,9 @@ import android.view.inputmethod.ExtractedText
import android.view.inputmethod.ExtractedTextRequest
import android.view.inputmethod.InputBinding
import android.view.inputmethod.InputConnection
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.text.isDigitsOnly
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
@@ -92,8 +95,7 @@ class EditorInstance(private val ims: InputMethodService) {
get() = if (isInputBindingActive) ims.currentInputBinding else null
val inputConnection: InputConnection?
get() = if (isInputBindingActive) ims.currentInputConnection else null
val editorInfo: EditorInfo?
get() = if (isInputBindingActive && ims.currentInputStarted) ims.currentInputEditorInfo else null
var editorInfo: EditorInfo? by mutableStateOf(null)
val cursorCapsMode: InputAttributes.CapsMode
get() {
@@ -172,6 +174,7 @@ class EditorInstance(private val ims: InputMethodService) {
fun startInput(info: EditorInfo) {
flogInfo(LogTopic.EDITOR_INSTANCE) { "info=${info.debugSummarize()}" }
editorInfo = info
if (AndroidVersion.ATLEAST_API25_N_MR1) {
contentMimeTypes = info.contentMimeTypes
}
@@ -191,6 +194,7 @@ class EditorInstance(private val ims: InputMethodService) {
fun startInputView(info: EditorInfo) {
flogInfo(LogTopic.EDITOR_INSTANCE) { "info=${info.debugSummarize()}" }
editorInfo = info
val keyboardMode = when (activeState.inputAttributes.type) {
InputAttributes.Type.NUMBER -> {
activeState.keyVariation = KeyVariation.NORMAL
@@ -248,10 +252,12 @@ class EditorInstance(private val ims: InputMethodService) {
fun finishInputView() {
flogInfo(LogTopic.EDITOR_INSTANCE) { "(no args)" }
editorInfo = null
}
fun finishInput() {
flogInfo(LogTopic.EDITOR_INSTANCE) { "(no args)" }
editorInfo = null
val ic = inputConnection ?: return
ic.requestCursorUpdates(CURSOR_UPDATE_DISABLED)
}
@@ -400,14 +406,17 @@ class EditorInstance(private val ims: InputMethodService) {
if (item == null) return false
val mimeTypes = item.mimeTypes
return when (item.type) {
ItemType.IMAGE -> {
ItemType.TEXT -> {
commitText(item.text.toString())
}
ItemType.IMAGE, ItemType.VIDEO -> {
item.uri ?: return false
val id = ContentUris.parseId(item.uri)
val file = ClipboardFileStorage.getFileForId(ims, id)
if (!file.exists()) return false
val inputContentInfo = InputContentInfoCompat(
item.uri,
ClipDescription("clipboard image", mimeTypes),
ClipDescription("clipboard media file", mimeTypes),
null,
)
val ic = inputConnection ?: return false
@@ -424,9 +433,6 @@ class EditorInstance(private val ims: InputMethodService) {
}
InputConnectionCompat.commitContent(ic, editorInfo!!, inputContentInfo, flags, null)
}
ItemType.TEXT -> {
commitText(item.text.toString())
}
}
}

View File

@@ -125,6 +125,12 @@ fun ComputingEvaluator.computeIconResId(data: KeyData): Int? {
KeyCode.ARROW_RIGHT -> {
R.drawable.ic_keyboard_arrow_right
}
KeyCode.ARROW_UP -> {
R.drawable.ic_keyboard_arrow_up
}
KeyCode.ARROW_DOWN -> {
R.drawable.ic_keyboard_arrow_down
}
KeyCode.CLIPBOARD_COPY -> {
R.drawable.ic_content_copy
}

View File

@@ -31,9 +31,10 @@ import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.common.ViewUtils
import dev.patrickgold.florisboard.common.observeAsTransformingState
import dev.patrickgold.florisboard.common.android.isOrientationLandscape
import dev.patrickgold.florisboard.common.observeAsTransformingState
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.jetpref.datastore.model.observeAsState
private val LocalKeyboardRowBaseHeight = staticCompositionLocalOf { 65.dp }
@@ -50,6 +51,28 @@ object FlorisImeSizing {
@ReadOnlyComposable
get() = LocalSmartbarHeight.current
@Composable
fun keyboardUiHeight(): Dp {
val prefs by florisPreferenceModel()
val numberRowEnabled by prefs.keyboard.numberRow.observeAsState()
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
val secondaryRowEnabled by prefs.smartbar.secondaryActionsEnabled.observeAsState()
val secondaryRowExpanded by prefs.smartbar.secondaryActionsExpanded.observeAsState()
val secondaryRowPlacement by prefs.smartbar.secondaryActionsPlacement.observeAsState()
val height =
if (smartbarEnabled) {
if (secondaryRowEnabled && secondaryRowExpanded &&
secondaryRowPlacement != SecondaryRowPlacement.OVERLAY_APP_UI) {
smartbarHeight * 2
} else {
smartbarHeight
}
} else {
0.dp
} + (keyboardRowBaseHeight * (if (numberRowEnabled) 5 else 4))
return height
}
object Static {
var keyboardRowBaseHeightPx: Int = 0

View File

@@ -21,6 +21,7 @@ import android.inputmethodservice.InputMethodService
import android.media.AudioManager
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.provider.Settings
import android.view.HapticFeedbackConstants
import androidx.compose.runtime.Composable
@@ -33,6 +34,7 @@ import dev.patrickgold.florisboard.debug.flogDebug
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.systemServiceOrNull
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -50,19 +52,19 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
@Composable
fun hasAmplitudeControl(): Boolean {
val vibrator = LocalContext.current.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
val vibrator = LocalContext.current.systemVibratorOrNull()
return when {
AndroidVersion.ATLEAST_API26_O -> vibrator?.hasAmplitudeControl() ?: false
AndroidVersion.ATLEAST_API26_O -> vibrator != null && vibrator.hasAmplitudeControl()
else -> false
}
}
@Composable
fun generateVibrationStrengthErrorSummary(): String? {
val vibrator = LocalContext.current.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
val vibrator = LocalContext.current.systemVibratorOrNull()
return when {
AndroidVersion.ATLEAST_API26_O -> when {
!(vibrator?.hasAmplitudeControl() ?: false) -> {
vibrator == null || !vibrator.hasAmplitudeControl() -> {
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_amplitude_ctrl)
}
else -> null
@@ -72,12 +74,20 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
}
}
}
private fun Context.systemVibratorOrNull(): Vibrator? {
return if (AndroidVersion.ATLEAST_API31_S) {
this.systemServiceOrNull(VibratorManager::class)?.defaultVibrator
} else {
this.systemServiceOrNull(Vibrator::class)
}
}
}
private val prefs by florisPreferenceModel()
private val audioManager = ims.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
private val vibrator = ims.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
private val audioManager = ims.systemServiceOrNull(AudioManager::class)
private val vibrator = ims.systemVibratorOrNull()
private val contentResolver = ims.contentResolver
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

View File

@@ -129,6 +129,11 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
prefs.keyboard.utilityKeyEnabled.observeForever {
updateRenderInfo()
}
prefs.glide.enabled.observeForever { enabled ->
if (enabled) {
glideTypingManager.setWordData(subtypeManager.activeSubtype())
}
}
activeState.observeForever {
updateRenderInfo()
}
@@ -282,7 +287,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
/**
* Changes a word to the current case.
* eg if [KeyboardState.capsLock] is true, abc -> ABC
* eg if [KeyboardState.isUppercase] is true, abc -> ABC
* if [caps] is true, abc -> Abc
* otherwise , abc -> abc
*/

View File

@@ -98,8 +98,6 @@ class KeyboardState private constructor(initValue: ULong) : LiveData<KeyboardSta
const val M_IME_UI_MODE: ULong = 0x07u
const val O_IME_UI_MODE: Int = 24
const val F_CAPS: ULong = 0x00000100u
const val F_CAPS_LOCK: ULong = 0x00000200u
const val F_IS_SELECTION_MODE: ULong = 0x00000400u
const val F_IS_MANUAL_SELECTION_MODE: ULong = 0x00000800u
const val F_IS_MANUAL_SELECTION_MODE_START: ULong = 0x00001000u
@@ -282,16 +280,6 @@ class KeyboardState private constructor(initValue: ULong) : LiveData<KeyboardSta
get() = if (getFlag(F_IS_RTL_LAYOUT_DIRECTION)) LayoutDirection.Rtl else LayoutDirection.Ltr
set(v) { setFlag(F_IS_RTL_LAYOUT_DIRECTION, v == LayoutDirection.Rtl) }
@Deprecated("Use inputMode and/or isLowercase/isUppercase")
var shiftLock: Boolean
get() = getFlag(F_CAPS)
set(v) { setFlag(F_CAPS, v) }
@Deprecated("Use inputMode and/or isLowercase/isUppercase")
var capsLock: Boolean
get() = getFlag(F_CAPS_LOCK)
set(v) { setFlag(F_CAPS_LOCK, v) }
val isLowercase: Boolean
get() = inputMode == InputMode.NORMAL

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Patrick Goldinger
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
package dev.patrickgold.florisboard.ime.lifecycle
import android.inputmethodservice.InputMethodService
import android.view.View
import androidx.annotation.CallSuper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -63,13 +62,23 @@ open class LifecycleInputMethodService : InputMethodService(),
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
@CallSuper
override fun onCreateInputView(): View? {
fun installViewTreeOwners() {
val decorView = window!!.window!!.decorView
ViewTreeLifecycleOwner.set(decorView, this)
ViewTreeViewModelStoreOwner.set(decorView, this)
ViewTreeSavedStateRegistryOwner.set(decorView, this)
return null
}
@CallSuper
override fun onWindowShown() {
super.onWindowShown()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
@CallSuper
override fun onWindowHidden() {
super.onWindowHidden()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
@CallSuper

View File

@@ -46,7 +46,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.InputEventDispatcher
import dev.patrickgold.florisboard.ime.core.InputKeyEvent
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
@@ -54,12 +53,10 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.media.emoji.EmojiPaletteView
import dev.patrickgold.florisboard.ime.media.emoji.parseRawEmojiSpecsFile
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.snygg.ui.SnyggSurface
import dev.patrickgold.jetpref.datastore.model.observeAsState
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -68,36 +65,19 @@ import kotlinx.coroutines.launch
fun MediaInputLayout(
modifier: Modifier = Modifier,
) {
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
val secondaryRowEnabled by prefs.smartbar.secondaryActionsEnabled.observeAsState()
val secondaryRowExpanded by prefs.smartbar.secondaryActionsExpanded.observeAsState()
val secondaryRowPlacement by prefs.smartbar.secondaryActionsPlacement.observeAsState()
val height =
if (smartbarEnabled) {
if (secondaryRowEnabled && secondaryRowExpanded &&
secondaryRowPlacement != SecondaryRowPlacement.OVERLAY_APP_UI) {
FlorisImeSizing.smartbarHeight * 2
} else {
FlorisImeSizing.smartbarHeight
}
} else {
0.dp
} + (FlorisImeSizing.keyboardRowBaseHeight * 4)
Column(
modifier = modifier
.fillMaxWidth()
.height(height),
) {
EmojiPaletteView(
modifier = Modifier.weight(1f),
fullEmojiMappings = parseRawEmojiSpecsFile(context, "ime/media/emoji/root.txt"),
)
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Column(
modifier = modifier
.fillMaxWidth()
.height(FlorisImeSizing.keyboardUiHeight()),
) {
EmojiPaletteView(
modifier = Modifier.weight(1f),
fullEmojiMappings = parseRawEmojiSpecsFile(context, "ime/media/emoji/root.txt"),
)
Row(
modifier = Modifier
.fillMaxWidth()

View File

@@ -85,7 +85,6 @@ import dev.patrickgold.florisboard.common.android.showShortToast
import dev.patrickgold.florisboard.common.kotlin.tryOrNull
import dev.patrickgold.florisboard.ime.core.InputKeyEvent
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.text.keyboard.fontSizeMultiplier
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
@@ -324,6 +323,7 @@ private fun EmojiKey(
modifier = Modifier.align(Alignment.Center),
text = base.value,
emojiCompatInstance = emojiCompatInstance,
color = contentColor,
fontSize = fontSize,
)
if (variations.isNotEmpty()) {
@@ -398,6 +398,7 @@ private fun EmojiVariationsPopup(
modifier = Modifier.align(Alignment.Center),
text = emoji.value,
emojiCompatInstance = emojiCompatInstance,
color = popupStyle.foreground.solidColor(default = FlorisImeTheme.fallbackContentColor()),
fontSize = popupStyle.fontSize.spSize(default = EmojiDefaultFontSize) safeTimes fontSizeMultiplier,
)
}
@@ -412,6 +413,7 @@ fun EmojiText(
text: String,
emojiCompatInstance: EmojiCompat?,
modifier: Modifier = Modifier,
color: Color = Color.Black,
fontSize: TextUnit = EmojiDefaultFontSize,
) {
if (emojiCompatInstance != null) {
@@ -420,7 +422,7 @@ fun EmojiText(
factory = { context ->
EmojiTextView(context).also {
it.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize.value)
it.setTextColor(Color.Black.toArgb())
it.setTextColor(color.toArgb())
}
},
update = { view ->
@@ -433,7 +435,7 @@ fun EmojiText(
factory = { context ->
TextView(context).also {
it.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize.value)
it.setTextColor(Color.Black.toArgb())
it.setTextColor(color.toArgb())
}
},
update = { view ->

View File

@@ -40,13 +40,13 @@ fun TextInputLayout(
val renderInfo by keyboardManager.renderInfo.observeAsNonNullState()
Column(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
) {
Smartbar()
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Column(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
) {
Smartbar()
TextKeyboardLayout(
renderInfo = renderInfo,
)

View File

@@ -95,6 +95,10 @@ class HangulUnicode : Composer {
val tple = medialComp[medials[med]]
return Pair(1, "${syllable(ini, medials.indexOf(tple!![1][tple[0].indexOf(c)]), 0)}")
}
} else if (lastChar in medialComp.keys && medialComp[lastChar]?.get(0)?.contains(c) == true) { // medial+final
return Pair(1, ""+ medialComp[lastChar]?.get(1)!![medialComp[lastChar]?.get(0)!!.indexOf(c)]);
} else if (lastChar in finalComp.keys && finalComp[lastChar]?.get(0)?.contains(c) == true) { // final+final
return Pair(1, ""+ finalComp[lastChar]?.get(1)!![finalComp[lastChar]?.get(0)!!.indexOf(c)]);
}
return Pair(0, ""+c)

View File

@@ -3,6 +3,7 @@ package dev.patrickgold.florisboard.ime.text.gestures
import android.content.Context
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.assetManager
import dev.patrickgold.florisboard.common.kotlin.guardedByLock
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.nlp.SuggestionList
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
@@ -12,7 +13,7 @@ import dev.patrickgold.florisboard.res.FlorisRef
import dev.patrickgold.florisboard.subtypeManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
@@ -22,7 +23,7 @@ import kotlin.math.min
* Handles the [GlideTypingClassifier]. Basically responsible for linking [GlideTypingGesture.Detector]
* with [GlideTypingClassifier].
*/
class GlideTypingManager(context: Context) : GlideTypingGesture.Listener, CoroutineScope by MainScope() {
class GlideTypingManager(context: Context) : GlideTypingGesture.Listener {
companion object {
private const val MAX_SUGGESTION_COUNT = 8
}
@@ -33,9 +34,10 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener, Corout
private val nlpManager by context.nlpManager()
private val subtypeManager by context.subtypeManager()
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private var glideTypingClassifier = StatisticalGlideTypingClassifier()
private var lastTime = System.currentTimeMillis()
private val wordDataCache = hashMapOf<String, Int>()
private val wordDataCache = guardedByLock { hashMapOf<String, Int>() }
override fun onGlideComplete(data: GlideTypingGesture.Detector.PointerData) {
updateSuggestionsAsync(MAX_SUGGESTION_COUNT, true) {
@@ -72,15 +74,16 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener, Corout
* Set the word data for the internal gesture classifier
*/
fun setWordData(subtype: Subtype) {
launch(Dispatchers.Default) {
if (wordDataCache.isEmpty()) {
// FIXME: get this info from dictionary.
val data = assetManager.loadTextAsset(FlorisRef.assets("ime/dict/data.json"))
.getOrThrow()
val json = JSONObject(data)
wordDataCache.putAll(json.keys().asSequence().map { Pair(it, json.getInt(it)) })
scope.launch(Dispatchers.Default) {
wordDataCache.withLock { wordDataCache ->
if (wordDataCache.isEmpty()) {
// FIXME: get this info from dictionary.
val data = assetManager.loadTextAsset(FlorisRef.assets("ime/dict/data.json")).getOrThrow()
val json = JSONObject(data)
wordDataCache.putAll(json.keys().asSequence().map { it to json.getInt(it) })
}
glideTypingClassifier.setWordData(wordDataCache, subtype)
}
glideTypingClassifier.setWordData(wordDataCache, subtype)
}
}
@@ -98,7 +101,7 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener, Corout
return
}
launch(Dispatchers.Default) {
scope.launch(Dispatchers.Default) {
val suggestions = glideTypingClassifier.getSuggestions(MAX_SUGGESTION_COUNT, true)
withContext(Dispatchers.Main) {

View File

@@ -124,7 +124,7 @@ class StatisticalGlideTypingClassifier : GlideTypingClassifier {
return
}
this.words = words.keys
this.words = words.keys.toSet()
this.wordFrequencies = words
this.wordDataSubtype = subtype

View File

@@ -226,7 +226,21 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
foregroundDrawableId = evaluator.computeIconResId(computedData)
val data = computedData
if (data.type == KeyType.CHARACTER && data.code != KeyCode.SPACE && data.code != KeyCode.CJK_SPACE
if (data.type == KeyType.NUMERIC && evaluator.keyboard().mode == KeyboardMode.PHONE) {
hintedLabel = when (data.code) {
48 /* 0 */ -> "+"
49 /* 1 */ -> ""
50 /* 2 */ -> "ABC"
51 /* 3 */ -> "DEF"
52 /* 4 */ -> "GHI"
53 /* 5 */ -> "JKL"
54 /* 6 */ -> "MNO"
55 /* 7 */ -> "PQRS"
56 /* 8 */ -> "TUV"
57 /* 9 */ -> "WXYZ"
else -> null
}
} else if (data.type == KeyType.CHARACTER && data.code != KeyCode.SPACE && data.code != KeyCode.CJK_SPACE
&& data.code != KeyCode.HALF_SPACE && data.code != KeyCode.KESHIDA || data.type == KeyType.NUMERIC
) {
val prefs by florisPreferenceModel()

View File

@@ -17,6 +17,7 @@
package dev.patrickgold.florisboard.ime.text.keyboard
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.Unicode
import dev.patrickgold.florisboard.common.kotlin.lowercase
import dev.patrickgold.florisboard.common.kotlin.uppercase
import dev.patrickgold.florisboard.ime.keyboard.AbstractKeyData
@@ -25,7 +26,9 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.popup.PopupSet
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
import kotlinx.serialization.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
/**
* Data class which describes a single key and its attributes.
@@ -60,9 +63,7 @@ class TextKeyData(
override fun asString(isForDisplay: Boolean): String {
return buildString {
if (isForDisplay || code == KeyCode.URI_COMPONENT_TLD || code < KeyCode.SPACE) {
// Combining Diacritical Marks
// See: https://en.wikipedia.org/wiki/Combining_Diacritical_Marks
if (code in 0x0300..0x036F && !label.startsWith("")) {
if (Unicode.isNonSpacingMark(code) && !label.startsWith("")) {
append("")
}
append(label)
@@ -76,8 +77,84 @@ class TextKeyData(
return "${TextKeyData::class.simpleName} { type=$type code=$code label=\"$label\" groupId=$groupId }"
}
@Suppress("UNUSED")
@Suppress("MemberVisibilityCanBePrivate")
companion object {
fun getCodeInfoAsTextKeyData(code: Int): TextKeyData? {
return if (code <= 0) {
InternalKeys.find { it.code == code }
} else {
TextKeyData(
type = KeyType.CHARACTER,
code = code,
label = buildString {
try {
appendCodePoint(code)
} catch (_: Throwable) {
}
},
)
}
}
// TODO: find better solution than to hand define array of below keys...
private val InternalKeys by lazy {
listOf(
UNSPECIFIED,
SPACE,
CTRL,
CTRL_LOCK,
ALT,
ALT_LOCK,
FN,
FN_LOCK,
DELETE,
DELETE_WORD,
FORWARD_DELETE,
FORWARD_DELETE_WORD,
SHIFT,
SHIFT_LOCK,
CAPS_LOCK,
ARROW_LEFT,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
MOVE_START_OF_PAGE,
MOVE_END_OF_PAGE,
MOVE_START_OF_LINE,
MOVE_END_OF_LINE,
CLIPBOARD_COPY,
CLIPBOARD_CUT,
CLIPBOARD_PASTE,
CLIPBOARD_SELECT,
CLIPBOARD_SELECT_ALL,
CLIPBOARD_CLEAR_HISTORY,
CLIPBOARD_CLEAR_FULL_HISTORY,
CLIPBOARD_CLEAR_PRIMARY_CLIP,
COMPACT_LAYOUT_TO_LEFT,
COMPACT_LAYOUT_TO_RIGHT,
UNDO,
REDO,
VIEW_CHARACTERS,
VIEW_SYMBOLS,
VIEW_SYMBOLS2,
VIEW_NUMERIC_ADVANCED,
IME_UI_MODE_TEXT,
IME_UI_MODE_MEDIA,
IME_UI_MODE_CLIPBOARD,
SYSTEM_INPUT_METHOD_PICKER,
SYSTEM_PREV_INPUT_METHOD,
SYSTEM_NEXT_INPUT_METHOD,
IME_SUBTYPE_PICKER,
IME_PREV_SUBTYPE,
IME_NEXT_SUBTYPE,
LANGUAGE_SWITCH,
IME_SHOW_UI,
IME_HIDE_UI,
SETTINGS,
INTERNAL_BATCH_EDIT,
)
}
/** Predefined key data for [KeyCode.UNSPECIFIED] */
val UNSPECIFIED = TextKeyData(
type = KeyType.UNSPECIFIED,
@@ -375,6 +452,12 @@ class TextKeyData(
code = KeyCode.IME_NEXT_SUBTYPE,
label = "ime_next_subtype",
)
/** Predefined key data for [KeyCode.LANGUAGE_SWITCH] */
val LANGUAGE_SWITCH = TextKeyData(
type = KeyType.SYSTEM_GUI,
code = KeyCode.LANGUAGE_SWITCH,
label = "language_switch",
)
/** Predefined key data for [KeyCode.IME_SHOW_UI] */
val IME_SHOW_UI = TextKeyData(
@@ -432,9 +515,7 @@ class AutoTextKeyData(
override fun asString(isForDisplay: Boolean): String {
return buildString {
if (isForDisplay || code == KeyCode.URI_COMPONENT_TLD || code < KeyCode.SPACE) {
// Combining Diacritical Marks
// See: https://en.wikipedia.org/wiki/Combining_Diacritical_Marks
if (code in 0x0300..0x036F && !label.startsWith("")) {
if (Unicode.isNonSpacingMark(code) && !label.startsWith("")) {
append("")
}
append(label)

View File

@@ -20,8 +20,10 @@ import android.animation.ValueAnimator
import android.content.Context
import android.view.MotionEvent
import android.view.animation.AccelerateInterpolator
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -38,6 +40,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
@@ -56,14 +59,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.app.prefs.AppPrefs
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.ui.components.DisposableLifecycleEffect
import dev.patrickgold.florisboard.app.ui.components.safeTimes
import dev.patrickgold.florisboard.common.FlorisRect
import dev.patrickgold.florisboard.common.Pointer
import dev.patrickgold.florisboard.common.PointerMap
import dev.patrickgold.florisboard.common.android.isOrientationLandscape
import dev.patrickgold.florisboard.common.android.isOrientationPortrait
import dev.patrickgold.florisboard.common.observeAsTransformingState
import dev.patrickgold.florisboard.common.toIntOffset
import dev.patrickgold.florisboard.debug.LogTopic
@@ -73,7 +75,6 @@ import dev.patrickgold.florisboard.ime.core.InputKeyEvent
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.keyboard.RenderInfo
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.popup.ExceptionsForKeyCodes
import dev.patrickgold.florisboard.ime.popup.PopupUiController
import dev.patrickgold.florisboard.ime.popup.rememberPopupUiController
@@ -115,12 +116,21 @@ fun TextKeyboardLayout(
val configuration = LocalConfiguration.current
val glideTypingManager by context.glideTypingManager()
val keyboard = renderInfo.keyboard
val numberRowEnabled by prefs.keyboard.numberRow.observeAsTransformingState { numberRowEnabled ->
when (keyboard.mode) {
KeyboardMode.CHARACTERS,
KeyboardMode.NUMERIC_ADVANCED,
KeyboardMode.SYMBOLS,
KeyboardMode.SYMBOLS2 -> numberRowEnabled
else -> false
}
}
val glideEnabled by prefs.glide.enabled.observeAsState()
val glideShowTrail by prefs.glide.showTrail.observeAsState()
val glideTrailColor = FlorisImeTheme.style.get(element = FlorisImeUi.GlideTrail)
.foreground.solidColor(default = Color.Green)
val keyboard = renderInfo.keyboard
val controller = remember { TextKeyboardLayoutController(context) }.also {
it.keyboard = keyboard
if (glideEnabled && !isSmartbarKeyboard && !isPreview && keyboard.mode == KeyboardMode.CHARACTERS) {
@@ -130,15 +140,32 @@ fun TextKeyboardLayout(
}
val touchEventChannel = remember { Channel<MotionEvent>(64) }
fun resetAllKeys() {
try {
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_CANCEL, 0f, 0f, 0)
controller.onTouchEventInternal(event)
controller.popupUiController.hide()
event.recycle()
} catch (e: Throwable) {
// Ignore
}
}
DisposableEffect(Unit) {
controller.glideTypingDetector.registerListener(controller)
controller.glideTypingDetector.registerListener(glideTypingManager)
onDispose {
controller.glideTypingDetector.unregisterListener(controller)
controller.glideTypingDetector.unregisterListener(glideTypingManager)
resetAllKeys()
}
}
DisposableLifecycleEffect(
onResume = { /* Do nothing */ },
onPause = { resetAllKeys() },
)
BoxWithConstraints(
modifier = modifier
.fillMaxWidth()
@@ -146,7 +173,8 @@ fun TextKeyboardLayout(
if (isSmartbarKeyboard) {
FlorisImeSizing.smartbarHeight
} else {
FlorisImeSizing.keyboardRowBaseHeight * keyboard.rowCount
FlorisImeSizing.keyboardRowBaseHeight *
keyboard.rowCount.coerceAtLeast(if (numberRowEnabled) 5 else 4)
}
)
.onGloballyPositioned { coords ->
@@ -212,7 +240,8 @@ fun TextKeyboardLayout(
height = FlorisImeSizing.smartbarHeight.toPx()
} else {
width = keyboardWidth / 10f
height = FlorisImeSizing.keyboardRowBaseHeight.toPx()
height = FlorisImeSizing.keyboardRowBaseHeight.toPx() *
(if (numberRowEnabled && keyboard.mode != KeyboardMode.CHARACTERS) 1.12f else 1f)
}
}
desiredKey.visibleBounds.applyFrom(desiredKey.touchBounds).deflateBy(keyMarginH, keyMarginV)
@@ -257,7 +286,8 @@ fun TextKeyboardLayout(
val c = key.computedData.code
val t = key.computedData.type
val numeric = keyboard.mode == KeyboardMode.NUMERIC ||
keyboard.mode == KeyboardMode.NUMERIC_ADVANCED && t == KeyType.NUMERIC
keyboard.mode == KeyboardMode.PHONE || keyboard.mode == KeyboardMode.PHONE2 ||
keyboard.mode == KeyboardMode.NUMERIC_ADVANCED && t == KeyType.NUMERIC
c > KeyCode.SPACE && c != KeyCode.MULTIPLE_CODE_POINTS && c != KeyCode.CJK_SPACE && !numeric
} else {
true
@@ -314,13 +344,29 @@ private fun TextKeyButton(
KeyCode.VIEW_NUMERIC_ADVANCED -> 0.55f
else -> 1.0f
}
SnyggSurface(
val size = key.visibleBounds.size.toDpSize()
Box(
modifier = Modifier
.requiredSize(key.visibleBounds.size.toDpSize())
.requiredSize(size)
.absoluteOffset { key.visibleBounds.topLeft.toIntOffset() },
style = keyStyle,
clip = true,
) {
// TODO: maybe make this customizable through a size property for keyStyle
val isReducedHeight = key.computedData.let { it.code == KeyCode.ENTER || it.code == KeyCode.SPACE }
SnyggSurface(
modifier = Modifier
.fillMaxWidth()
.run {
if (isReducedHeight && FlorisImeTheme.config.isBorderless) {
this.padding(vertical = size.height * 0.15f)
} else {
this
}
}
.fillMaxHeight(),
style = keyStyle,
clip = false,
) { }
val isTelpadKey = key.computedData.type == KeyType.NUMERIC && renderInfo.keyboard.mode == KeyboardMode.PHONE
key.label?.let { label ->
if (key.computedData.code == KeyCode.SPACE) {
val prefs by florisPreferenceModel()
@@ -332,7 +378,7 @@ private fun TextKeyButton(
Text(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center),
.align(if (isTelpadKey) BiasAlignment(-0.5f, 0f) else Alignment.Center),
text = label,
color = keyStyle.foreground.solidColor(),
fontSize = fontSize,
@@ -354,10 +400,10 @@ private fun TextKeyButton(
val hintFontSize = keyHintStyle.fontSize.spSize() safeTimes fontSizeMultiplier
Text(
modifier = Modifier
.padding(end = (key.visibleBounds.width / 12f).toDp())
.wrapContentSize()
.align(Alignment.TopEnd)
.snyggBackground(keyHintStyle),
.align(if (isTelpadKey) BiasAlignment(0.5f, 0f) else Alignment.TopEnd)
.snyggBackground(keyHintStyle)
.padding(horizontal = (key.visibleBounds.width / 12f).toDp()),
text = hintedLabel,
color = keyHintStyle.foreground.solidColor(),
fontFamily = FontFamily.Monospace,
@@ -398,7 +444,7 @@ private class TextKeyboardLayoutController(
private var initSelectionStart: Int = 0
private var initSelectionEnd: Int = 0
var isGliding: Boolean = false
var isGliding by mutableStateOf(false)
val glideTypingDetector = GlideTypingGesture.Detector(context)
val glideDataForDrawing = mutableStateListOf<Pair<GlideTypingGesture.Detector.Position, Long>>()
@@ -782,7 +828,7 @@ private class TextKeyboardLayoutController(
inputFeedbackController?.gestureMovingSwipe(TextKeyData.DELETE)
}
markComposingRegion(null)
if (selection.isValid) {
if (selection.isValid && event.absUnitCountX <= 0) {
selectionSetNWordsLeft(abs(event.absUnitCountX / 2) - 1)
}
}
@@ -810,7 +856,8 @@ private class TextKeyboardLayoutController(
return when (event.type) {
SwipeGesture.Type.TOUCH_MOVE -> when (event.direction) {
SwipeGesture.Direction.LEFT -> {
if (prefs.gestures.spaceBarSwipeLeft.get() == SwipeAction.MOVE_CURSOR_LEFT) {
val action = prefs.gestures.spaceBarSwipeLeft.get()
if (action == SwipeAction.MOVE_CURSOR_LEFT) {
abs(event.relUnitCountX).let {
val count = if (!pointer.hasTriggeredGestureMove) {
it - 1
@@ -826,11 +873,14 @@ private class TextKeyboardLayoutController(
)
}
}
true
} else {
action != SwipeAction.NO_ACTION
}
true
}
SwipeGesture.Direction.RIGHT -> {
if (prefs.gestures.spaceBarSwipeRight.get() == SwipeAction.MOVE_CURSOR_RIGHT) {
val action = prefs.gestures.spaceBarSwipeRight.get()
if (action == SwipeAction.MOVE_CURSOR_RIGHT) {
abs(event.relUnitCountX).let {
val count = if (!pointer.hasTriggeredGestureMove) {
it - 1
@@ -846,29 +896,43 @@ private class TextKeyboardLayoutController(
)
}
}
true
} else {
action != SwipeAction.NO_ACTION
}
true
}
else -> true // To prevent the popup display of nearby keys
}
SwipeGesture.Type.TOUCH_UP -> when (event.direction) {
SwipeGesture.Direction.LEFT -> {
prefs.gestures.spaceBarSwipeLeft.get().let {
if (it != SwipeAction.MOVE_CURSOR_LEFT) {
keyboardManager.executeSwipeAction(it)
true
} else {
false
when {
it == SwipeAction.NO_ACTION -> {
false
}
it != SwipeAction.MOVE_CURSOR_LEFT -> {
keyboardManager.executeSwipeAction(it)
true
}
else -> {
false
}
}
}
}
SwipeGesture.Direction.RIGHT -> {
prefs.gestures.spaceBarSwipeRight.get().let {
if (it != SwipeAction.MOVE_CURSOR_RIGHT) {
keyboardManager.executeSwipeAction(it)
true
} else {
false
when {
it == SwipeAction.NO_ACTION -> {
false
}
it != SwipeAction.MOVE_CURSOR_RIGHT -> {
keyboardManager.executeSwipeAction(it)
true
}
else -> {
false
}
}
}
}
@@ -970,21 +1034,3 @@ private class TextKeyboardLayoutController(
}
}
}
@Composable
fun AppPrefs.Keyboard.fontSizeMultiplier(): Float {
val configuration = LocalConfiguration.current
val oneHandedMode by oneHandedMode.observeAsState()
val oneHandedModeFactor by oneHandedModeScaleFactor.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplierBase by if (configuration.isOrientationPortrait()) {
fontSizeMultiplierPortrait
} else {
fontSizeMultiplierLandscape
}.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplier = fontSizeMultiplierBase * if (oneHandedMode != OneHandedMode.OFF && configuration.isOrientationPortrait()) {
oneHandedModeFactor
} else {
1.0f
}
return fontSizeMultiplier
}

View File

@@ -30,6 +30,10 @@ object FlorisImeUi {
const val EmojiKeyPopup = "emoji-key-popup"
const val EmojiTab = "emoji-tab"
const val ExtractedLandscapeInputLayout = "extracted-landscape-input-layout"
const val ExtractedLandscapeInputField = "extracted-landscape-input-field"
const val ExtractedLandscapeInputAction = "extracted-landscape-input-action"
const val GlideTrail = "glide-trail"
const val OneHandedPanel = "one-handed-panel"

View File

@@ -101,6 +101,7 @@ object FlorisImeUiSpec : SnyggSpec({
background()
foreground()
font()
shape()
}
element(FlorisImeUi.KeyPopup) {
background()
@@ -204,6 +205,22 @@ object FlorisImeUiSpec : SnyggSpec({
foreground()
}
element(FlorisImeUi.ExtractedLandscapeInputLayout) {
background()
}
element(FlorisImeUi.ExtractedLandscapeInputField) {
background()
foreground()
font()
shape()
border()
}
element(FlorisImeUi.ExtractedLandscapeInputAction) {
background()
foreground()
shape()
}
element(FlorisImeUi.GlideTrail) {
foreground()
}

View File

@@ -16,25 +16,76 @@
package dev.patrickgold.florisboard.util
import android.os.Bundle
import android.text.InputType
import android.text.TextUtils
import android.view.inputmethod.EditorInfo
import androidx.core.view.inputmethod.EditorInfoCompat
import dev.patrickgold.florisboard.common.android.AndroidVersion
import kotlin.reflect.KClass
private const val SECTION_SEPARATOR = "---"
fun EditorInfo.debugSummarize(): String {
return StringBuilder().let {
it.appendLine(this::class.qualifiedName)
it.append("imeOptions: ").appendLine(this.imeOptions.debugSummarize(EditorInfo::class))
it.append("initialCapsMode: ").appendLine(this.initialCapsMode.debugSummarize(TextUtils::class))
it.append("initialSelStart: ").appendLine(this.initialSelStart)
it.append("initialSelEnd: ").appendLine(this.initialSelEnd)
it.append("inputType: ").appendLine(this.inputType.debugSummarize(InputType::class))
it.append("packageName: ").appendLine(this.packageName)
it.toString()
val info = this
return buildString {
appendLine(info::class.qualifiedName)
append("packageName: ").appendLine(info.packageName)
appendLine(SECTION_SEPARATOR)
append("inputType: ").appendLine(info.inputType.debugSummarize(InputType::class))
append("imeOptions: ").appendLine(info.imeOptions.debugSummarize(EditorInfo::class))
append("privateImeOptions: ").appendLine(info.privateImeOptions ?: "(null)")
appendLine(SECTION_SEPARATOR)
append("actionId: ").appendLine(info.actionId.dsEditorInfoActionId())
append("actionLabel: ").appendLine(info.actionLabel ?: "(null)")
append("contentMimeTypes: ").appendLine(EditorInfoCompat.getContentMimeTypes(info).contentToString())
append("extras: ").appendLine(info.extras?.debugSummarize() ?: "(null)")
append("hintLocales: ").also {
if (AndroidVersion.ATLEAST_API24_N) {
appendLine(info.hintLocales?.toLanguageTags() ?: "(null)")
} else {
appendLine("(null)")
}
}
append("hintText: ").appendLine(info.hintText ?: "(null)")
appendLine(SECTION_SEPARATOR)
append("initialCapsMode: ").appendLine(info.initialCapsMode.debugSummarize(TextUtils::class))
append("initialSelStart: ").appendLine(info.initialSelStart)
append("initialSelEnd: ").appendLine(info.initialSelEnd)
}
}
fun <T: Any> Int.debugSummarize(type: KClass<T>): String {
private fun Bundle.debugSummarize(): String {
val bundle = this
return buildString {
append("[")
for ((i, key) in bundle.keySet().withIndex()) {
if (i > 0) {
append(",")
}
append(key)
append("=")
append(bundle.get(key))
}
append("]")
}
}
private fun Int.dsEditorInfoActionId(): String {
return when (this) {
EditorInfo.IME_ACTION_DONE -> "IME_ACTION_DONE"
EditorInfo.IME_ACTION_GO -> "IME_ACTION_GO"
EditorInfo.IME_ACTION_NEXT -> "IME_ACTION_NEXT"
EditorInfo.IME_ACTION_NONE -> "IME_ACTION_NONE"
EditorInfo.IME_ACTION_PREVIOUS -> "IME_ACTION_PREVIOUS"
EditorInfo.IME_ACTION_SEARCH -> "IME_ACTION_SEARCH"
EditorInfo.IME_ACTION_SEND -> "IME_ACTION_SEND"
EditorInfo.IME_ACTION_UNSPECIFIED -> "IME_ACTION_UNSPECIFIED"
else -> String.format("0x%08x", this)
}
}
private fun <T: Any> Int.debugSummarize(type: KClass<T>): String {
val summary = StringBuilder()
when (type) {
EditorInfo::class -> {
@@ -43,17 +94,7 @@ fun <T: Any> Int.debugSummarize(type: KClass<T>): String {
summary.append("IME_NULL")
}
else -> {
val tAction = when (this and EditorInfo.IME_MASK_ACTION) {
EditorInfo.IME_ACTION_DONE -> "IME_ACTION_DONE"
EditorInfo.IME_ACTION_GO -> "IME_ACTION_GO"
EditorInfo.IME_ACTION_NEXT -> "IME_ACTION_NEXT"
EditorInfo.IME_ACTION_NONE -> "IME_ACTION_NONE"
EditorInfo.IME_ACTION_PREVIOUS -> "IME_ACTION_PREVIOUS"
EditorInfo.IME_ACTION_SEARCH -> "IME_ACTION_SEARCH"
EditorInfo.IME_ACTION_SEND -> "IME_ACTION_SEND"
EditorInfo.IME_ACTION_UNSPECIFIED -> "IME_ACTION_UNSPECIFIED"
else -> String.format("0x%08x", this and EditorInfo.IME_MASK_ACTION)
}
val tAction = (this and EditorInfo.IME_MASK_ACTION).dsEditorInfoActionId()
val tFlags = StringBuilder()
if (this and EditorInfo.IME_FLAG_FORCE_ASCII > 0) {
tFlags.append("IME_FLAG_FORCE_ASCII|")

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:tintMode="multiply">
<solid android:color="@android:color/transparent"/>
<corners android:radius="@dimen/landscapeInputUi_editText_cornerRadius"/>
<stroke android:width="@dimen/landscapeInputUi_editText_borderWidth" android:color="@android:color/white"/>
</shape>

View File

@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M11.5,9C10.12,9 9,10.12 9,11.5s1.12,2.5 2.5,2.5 2.5,-1.12 2.5,-2.5S12.88,9 11.5,9zM20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM16.79,18.21l-2.91,-2.91c-0.69,0.44 -1.51,0.7 -2.39,0.7C9.01,16 7,13.99 7,11.5S9.01,7 11.5,7 16,9.01 16,11.5c0,0.88 -0.26,1.69 -0.7,2.39l2.91,2.9 -1.42,1.42z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M15,8v8H5V8h10m1,-2H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4V7c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>

View File

@@ -103,16 +103,23 @@
<string name="settings__theme_editor__no_rules_defined">ورق الأنماط هذا ليس له قواعد محددة. أضف قاعدة لبدء تخصيص ورق الأنماط هذا.</string>
<string name="settings__theme_editor__rule_already_exists">تم تعريف قاعدة ورقة الأنماط هذه بالفعل.</string>
<string name="settings__theme_editor__rule_element">العنصر المستهدف</string>
<string name="settings__theme_editor__rule_codes">التعليمات البرمجيه</string>
<string name="settings__theme_editor__rule_codes">رموز المفتاح المستهدف</string>
<string name="settings__theme_editor__rule_groups">مجموعات</string>
<string name="settings__theme_editor__rule_modes">أنماط</string>
<string name="settings__theme_editor__rule_selectors">محددات</string>
<string name="settings__theme_editor__add_code">إضافة كود</string>
<string name="settings__theme_editor__edit_code">تعديل الكود</string>
<string name="settings__theme_editor__no_codes_defined">لم يتم تعريف اي كود.</string>
<string name="settings__theme_editor__add_code">إضافة رموز المفتاح</string>
<string name="settings__theme_editor__edit_code">تعديل رمز المفتاح</string>
<string name="settings__theme_editor__no_codes_defined">تطبيق القاعدة على جميع العناصر المستهدفة.</string>
<string name="settings__theme_editor__codes_defined">تطبيق القاعدة فقط على العناصر المستهدفة ذات الرموز الرئيسية التالية:</string>
<string name="settings__theme_editor__code_already_exists">تم تعريف رمز المفتاح هذا بالفعل.</string>
<string name="settings__theme_editor__code_invalid">رمز المفتاح هذا غير صالح. تأكد من أن رمز المفتاح يقع في نطاق {c_min} إلى {c_max} للأحرف أو {i_min} إلى {i_max} للمفاتيح الخاصة الداخلية.</string>
<string name="settings__theme_editor__code_help_text">ستساعدك الروابط التالية في العثور على رمز المفتاح المقابل:</string>
<string name="settings__theme_editor__code_help_text">بدلاً من ذلك ، ستساعدك الروابط التالية في العثور على رمز المفتاح المقابل:</string>
<string name="settings__theme_editor__code_placeholder">رمز</string>
<string name="settings__theme_editor__code_recording_help_text">للعثور على رمز المفتاح ، استخدم الزر بجانب حقل إدخال الرمز. بمجرد تنشيطه ، سيتم تسجيل الضغط على المفتاح التالي وسيقوم بإدخال الرمز في حقل الإدخال.</string>
<string name="settings__theme_editor__code_recording_started">بدأ تسجيل رمز المفتاح</string>
<string name="settings__theme_editor__code_recording_stopped">توقف تسجيل رمز المفتاح</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">يجب أن يكون {app_name} هو لوحة المفاتيح الافتراضية لتسجيل رمز المفتاح</string>
<string name="settings__theme_editor__code_recording_placeholder">جارٍ التسجيل…</string>
<string name="settings__theme_editor__add_property">إضافة خاصية</string>
<string name="settings__theme_editor__edit_property">تحرير خاصية</string>
<string name="settings__theme_editor__property_already_exists">توجد خاصية بهذا الاسم بالفعل داخل القاعدة الحالية.</string>

View File

@@ -49,11 +49,12 @@
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">Клавиатурата FlorisBoard не е активна в системата и не е достъпна като метод за въвеждане. Докоснете тук, за да промените.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">Клавиатурата FlorisBoard не е избрана като подразбиран метод за въвеждане. Докоснете тук, за да направите промяна.</string>
<string name="settings__localization__title" comment="Title of languages and Layout screen">Езици и подредби</string>
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Показване на имената на езиците на</string>
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Изписване имената на езиците на</string>
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">Подредби</string>
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">Добавяне на подредба</string>
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">Променяне на подредба</string>
<string name="settings__localization__subtype_locale" comment="Label for locale dropdown in subtype dialog">Основен език</string>
<string name="settings__localization__subtype_popup_mapping" comment="Label for popup mapping dropdown in subtype screen">Подсказки</string>
<string name="settings__localization__subtype_characters_layout" comment="Label for layout dropdown in subtype dialog">Подредба на клавишите</string>
<string name="settings__localization__subtype_symbols_layout" comment="Label for layout dropdown in subtype dialog">Символи, основен изглед</string>
<string name="settings__localization__subtype_symbols2_layout" comment="Label for layout dropdown in subtype dialog">Символи, вторичен изглед</string>
@@ -102,16 +103,23 @@
<string name="settings__theme_editor__no_rules_defined">Стиловият лист няма правила. За да промените стиловете добавете правила.</string>
<string name="settings__theme_editor__rule_already_exists">Вече има такова правило.</string>
<string name="settings__theme_editor__rule_element">Целеви елемент</string>
<string name="settings__theme_editor__rule_codes">Кодове</string>
<string name="settings__theme_editor__rule_codes">Целеви кодове</string>
<string name="settings__theme_editor__rule_groups">Групи</string>
<string name="settings__theme_editor__rule_modes">Режим</string>
<string name="settings__theme_editor__rule_selectors">Селектор</string>
<string name="settings__theme_editor__add_code">Добавяне на код</string>
<string name="settings__theme_editor__edit_code">Промяна на код</string>
<string name="settings__theme_editor__no_codes_defined">Липсват кодове.</string>
<string name="settings__theme_editor__no_codes_defined">Правилото се прилага към всички целеви елементи.</string>
<string name="settings__theme_editor__codes_defined">Правилото се прилага само към целевите елементи със следните кодове:</string>
<string name="settings__theme_editor__code_already_exists">Кодът вече е дефиниран.</string>
<string name="settings__theme_editor__code_invalid">Кодът не е валиден. Уверете се че стойността му е между {c_min} и {c_max} за букви и между {i_min} и {i_max} за вградените спициални клавиши.</string>
<string name="settings__theme_editor__code_help_text">Следните препратки ще са ви от полза при намиране на клавишни кодове:</string>
<string name="settings__theme_editor__code_help_text">Следните препратки ще са ви от полза при намиране на кода на клавиш:</string>
<string name="settings__theme_editor__code_placeholder">Код на клавиш</string>
<string name="settings__theme_editor__code_recording_help_text">За да намерите кода на даден клавиш използвайте бутона до полето. Веднъж натиснат в полето ще се запише кода на следващия натиснат клавиш.</string>
<string name="settings__theme_editor__code_recording_started">Записът на кода на клавиш е започнат</string>
<string name="settings__theme_editor__code_recording_stopped">Записът на кода на клавиш е спрян</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">За да запише кода на клавиша, {app_name} трябва да е подразбираната клавиатура</string>
<string name="settings__theme_editor__code_recording_placeholder">Записване…</string>
<string name="settings__theme_editor__add_property">Добавяне на свойство</string>
<string name="settings__theme_editor__edit_property">Променяне на свойство</string>
<string name="settings__theme_editor__property_already_exists">Свойство с това име вече съществува в текущото правило.</string>
@@ -133,6 +141,16 @@
<string name="snygg__rule_element__clipboard_item_popup">Подсказка на елемент от междинната памет</string>
<string name="snygg__rule_element__glide_trail">Следа от плъзгане</string>
<string name="snygg__rule_element__one_handed_panel">Панел за работа с една ръка</string>
<string name="snygg__rule_element__smartbar_primary_row">Интелигентна лента, основен ред</string>
<string name="snygg__rule_element__smartbar_secondary_row">Интелигентна лента, допълнителен ред</string>
<string name="snygg__rule_element__smartbar_primary_actions_toggle">Интелигентна лента, бутон за превкл. на основната лента</string>
<string name="snygg__rule_element__smartbar_secondary_actions_toggle">Интелигентна лента, бутон за превкл. на допълнителната лента</string>
<string name="snygg__rule_element__smartbar_quick_action">Интелигентна лента, бързи действия</string>
<string name="snygg__rule_element__smartbar_key">Бутон за интелигентната лента</string>
<string name="snygg__rule_element__smartbar_candidate_word">Интелигентна лента, кандидат за дума</string>
<string name="snygg__rule_element__smartbar_candidate_clip">Интелигентна лента, контейнер на кандидата</string>
<string name="snygg__rule_element__smartbar_candidate_spacer">Интелигентна лента, разделител на кандидата</string>
<string name="snygg__rule_element__system_nav_bar">Интелигентна лента, лента за навигация</string>
<string name="snygg__rule_selector__pressed">Натиснат</string>
<string name="snygg__rule_selector__focus">На фокус</string>
<string name="snygg__rule_selector__disabled">Изключен</string>
@@ -226,7 +244,7 @@
<string name="pref__keyboard__group_keypress__label" comment="Preference group title">Докосване на клавишите</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">Видима изскачаща подсказка</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">Показва се изскачаща подсказка при докосване на клавиш</string>
<string name="pref__keyboard__merge_hint_popups_enabled__label" comment="Preference title">Акцентите в посдказките</string>
<string name="pref__keyboard__merge_hint_popups_enabled__label" comment="Preference title">Акцентите в подсказките</string>
<string name="pref__keyboard__merge_hint_popups_enabled__summary" comment="Preference summary">Добавят се подсказки със символи към подразбираната подредба</string>
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">Закъснение при задържане на клавиш</string>
<string name="pref__keyboard__space_bar_switches_to_characters__label" comment="Preference title">Клавишът интервал превключва към букви</string>
@@ -237,6 +255,7 @@
<string name="pref__smartbar__enabled__summary" comment="Preference summary">Показва се над първия ред</string>
<string name="pref__smartbar__flip_toggles__label" comment="Preference title">Разменяне на превключващите бутони</string>
<string name="pref__smartbar__flip_toggles__summary" comment="Preference summary">Разменят се бутоните, превключващи редовете</string>
<string name="pref__smartbar__any_row_type__label" comment="Preference title">Съдържание на реда</string>
<string name="pref__smartbar__group_primary_actions__label" comment="Preference group title">Основни действия</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__label" comment="Preference title">Автоматично разтваряне/събиране</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__summary" comment="Preference summary">Автоматично се разтваря/събира основния ред, на базата на състоянието</string>
@@ -247,10 +266,10 @@
<string name="pref__suggestion__title" comment="Preference group title">Предложения</string>
<string name="pref__suggestion__display_mode__label" comment="Preference title">Оформление на предложенията</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">Предложения от междинната памет</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">Времеви прозорец за предложения от междинната памет</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">Време за предложения от междинната памет</string>
<string name="pref__correction__title" comment="Preference group title">Корекции</string>
<string name="pref__correction__auto_capitalization__label" comment="Preference title">Автоматично поставяне на главни букви</string>
<string name="pref__correction__auto_capitalization__summary" comment="Preference summary">Думите се въвеждат с главна буква въз основа на текущия контекст</string>
<string name="pref__correction__auto_capitalization__label" comment="Preference title">Автоматично въвеждане на главни букви</string>
<string name="pref__correction__auto_capitalization__summary" comment="Preference summary">Използват се главни букви въз основа на контекста на въвеждане</string>
<string name="pref__correction__remember_caps_lock_state__label" comment="Preference title">Запомняне на състоянието на клавиш Caps Lock</string>
<string name="pref__correction__remember_caps_lock_state__summary" comment="Preference summary">Клавишът Caps Lock се оставя включен при преместване в друго текстово поле</string>
<string name="pref__correction__double_space_period__label" comment="Preference title">Точка при двоен интервал</string>
@@ -510,13 +529,19 @@
<string name="ext__editor__title_edit_theme">Промяна на добавка с тема</string>
<string name="ext__editor__metadata__title">Управление на мета данни</string>
<string name="ext__editor__metadata__title_invalid">Недействителни мета данни</string>
<string name="ext__editor__metadata__message_invalid">Описателните данни на резширението са недействителни. Проверете редактора за описателни данни за подробности!</string>
<string name="ext__editor__dependencies__title">Управление на зависимости</string>
<string name="ext__editor__files__title">Управление на архиви</string>
<string name="ext__editor__create_component__title">Създаване на компонент</string>
<string name="ext__editor__create_component__title_theme">Създаване на тема</string>
<string name="ext__editor__create_component__from_empty">Празна</string>
<string name="ext__editor__create_component__from_existing">От съществуваща</string>
<string name="ext__editor__edit_component__title">Редактиране на компонент</string>
<string name="ext__editor__edit_component__title_theme">Редактиране на компонент-тема</string>
<string name="ext__export__success">Темата е изнесена успешно!</string>
<string name="ext__export__failure">Грешка при изнасяне на разширение: {error_message}</string>
<string name="ext__import__success">Темата е внесена успешно!</string>
<string name="ext__import__failure">Грешка при внасяне на разширение: {error_message}</string>
<string name="ext__import__ext_any" comment="Title of Importer screen for import of any supported FlorisBoard extension">Внасяне на добавка</string>
<string name="ext__import__ext_keyboard" comment="Title of Importer screen for keyboard extension import">Внасяне на добавка с клавиатура</string>
<string name="ext__import__ext_spelling" comment="Title of Importer screen for spelling extension (or raw dictionary) import">Внасяне на добавка за правопис / речник</string>
@@ -584,8 +609,8 @@
<string name="enum__display_kbd_after_dialogs__never__description" comment="Enum value description">Клавиатурата не се показва никога след затваряне на диалог</string>
<string name="enum__display_kbd_after_dialogs__remember" comment="Enum value label">Запомняне на последното състояние</string>
<string name="enum__display_kbd_after_dialogs__remember__description" comment="Enum value description">Клавиатурата се показва след затваряне на диалог ако преди това е видима</string>
<string name="enum__display_language_names_in__system_locale" comment="Enum value label">Език на системата</string>
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Присъщ за езика</string>
<string name="enum__display_language_names_in__system_locale" comment="Enum value label">Езика на системата</string>
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Присъщия за езика</string>
<string name="enum__emoji_skin_tone__default" comment="Enum value label">{emoji} Подразбиран цвят на кожата</string>
<string name="enum__emoji_skin_tone__light_skin_tone" comment="Enum value label">{emoji} Светъл цвят на кожа</string>
<string name="enum__emoji_skin_tone__medium_light_skin_tone" comment="Enum value label">{emoji} Средно светъл цвят на кожата</string>
@@ -617,12 +642,15 @@
<string name="enum__secondary_row_placement__below_primary" comment="Enum value label">Под първия ред</string>
<string name="enum__secondary_row_placement__below_primary__description" comment="Enum value description">Добавя се втори ред между първия ред и клавиатурата</string>
<string name="enum__secondary_row_placement__overlay_app_ui" comment="Enum value label">Слой върху приложението</string>
<string name="enum__secondary_row_placement__overlay_app_ui__description" comment="Enum value description">Добавя се втори ред като плаващ слой над клавиатурата без да се увеличава височината ѝ. Имайте предвид, че по този начин полето за въвеждане може частично да бъде закрито</string>
<string name="enum__shape_corner__top_start" comment="Enum value label">Горе, начало</string>
<string name="enum__shape_corner__top_end" comment="Enum value label">Горе, край</string>
<string name="enum__shape_corner__bottom_end" comment="Enum value label">Долу, край</string>
<string name="enum__shape_corner__bottom_start" comment="Enum value label">Долу, начало</string>
<string name="enum__smartbar_row_type__quick_actions" comment="Enum value label">Бързи действия</string>
<string name="enum__smartbar_row_type__clipboard_cursor_tools" comment="Enum value label">Инструменти за междинна памет и каретка</string>
<string name="enum__smartbar_row_type__quick_actions__description" comment="Enum value description">Бързите действия, подредени в един ред с допълнително меню</string>
<string name="enum__smartbar_row_type__clipboard_cursor_tools" comment="Enum value label">Инструменти за междинната памет и каретката</string>
<string name="enum__smartbar_row_type__clipboard_cursor_tools__description" comment="Enum value description">Най-използваните действия с междинната памет и придвижване на каретката</string>
<string name="enum__snygg_level__basic" comment="Enum value label">Основен</string>
<string name="enum__snygg_level__basic__description" comment="Enum value description">Само цветове, свойствата и правилата са преведени.</string>
<string name="enum__snygg_level__advanced" comment="Enum value label">Разширен</string>

View File

@@ -88,13 +88,9 @@
<string name="settings__theme_manager__title_manage" comment="Title of the theme manager screen for managing installed and custom themes">Gestiona els temes instal·lats</string>
<string name="settings__theme_editor__add_rule">Afegir norma</string>
<string name="settings__theme_editor__edit_rule">Editar norma</string>
<string name="settings__theme_editor__rule_codes">Codis</string>
<string name="settings__theme_editor__rule_groups">Grups</string>
<string name="settings__theme_editor__rule_modes">Modes</string>
<string name="settings__theme_editor__rule_selectors">Selectors</string>
<string name="settings__theme_editor__add_code">Afegir codi</string>
<string name="settings__theme_editor__edit_code">Editar el codi</string>
<string name="settings__theme_editor__no_codes_defined">Cap codi definit.</string>
<string name="settings__theme_editor__add_property">Afegir propietat</string>
<string name="settings__theme_editor__edit_property">Editar propietat</string>
<string name="settings__theme_editor__property_name">Nom de la propietat</string>

View File

@@ -103,16 +103,23 @@
<string name="settings__theme_editor__no_rules_defined">ئەم پەڕەیە هیچ یاسایەکی پێناسە نەکراوە، زیادکردنی یاسایەک بۆ دەستکردن بە سازکردنی ئەم پەڕەیە.</string>
<string name="settings__theme_editor__rule_already_exists">ئەم یاسای پەڕەی ستایڵ پێشتر پێناسە کراوە.</string>
<string name="settings__theme_editor__rule_element">بەشی مەبەست</string>
<string name="settings__theme_editor__rule_codes">کۆد</string>
<string name="settings__theme_editor__rule_codes">سەرچاوەی کۆدی مەبەست</string>
<string name="settings__theme_editor__rule_groups">گرووپ</string>
<string name="settings__theme_editor__rule_modes">شێواز</string>
<string name="settings__theme_editor__rule_selectors">هەڵبژاردن</string>
<string name="settings__theme_editor__add_code">زیادکردنی کۆد</string>
<string name="settings__theme_editor__add_code">زیادکردنی کۆدی دوگمە</string>
<string name="settings__theme_editor__edit_code">دەستکاریکردنی کۆد</string>
<string name="settings__theme_editor__no_codes_defined">کۆد نەنوسراوە</string>
<string name="settings__theme_editor__no_codes_defined">جێبەجێکردن بۆ سەرجەم بەشەکان</string>
<string name="settings__theme_editor__codes_defined">جێبەجێ کردنی یاسا تەنها بۆ ئامانج توخمەکان لەگەڵ کۆدەکانی کلیلی خوارەوە:</string>
<string name="settings__theme_editor__code_already_exists">ئەم دوگمەیە پێشتر زیادکراوە</string>
<string name="settings__theme_editor__code_invalid">ئەم کۆدە کلیلە دروست نیە، دڵنیابە لەوەی کە کۆدی کلیلەکە لەناو مەودای {c_min} بۆ {c_max} بۆ پیتەکان یان {i_min} بۆ {i_max} بۆ کلیلە تایبەتەکان.</string>
<string name="settings__theme_editor__code_help_text">ئەم لینکانەی خوارەوە یارمەتیت دەدەن بۆ دۆزینەوەی کۆدی کلیلی هاوتا:</string>
<string name="settings__theme_editor__code_help_text">ئەم لینکانەی خوارەوە یارمەتیت ئەدەن بۆ دۆزینەوەی کۆدی کلیلی هاوتا:</string>
<string name="settings__theme_editor__code_placeholder">کۆد</string>
<string name="settings__theme_editor__code_recording_help_text">بۆ دۆزینەوەی کۆدی کلیلێک، دوگمەکە بەکاربهێنە جگە لە خانەی نوسینی کۆدەکە، کاتێک چالاک کرا، کلیلەکانی دواتر تۆمار دەکات و کۆدەکە دادەنێت بۆ خانەکە.</string>
<string name="settings__theme_editor__code_recording_started">دۆزینەوەی کۆد دەستیپێکرد</string>
<string name="settings__theme_editor__code_recording_stopped">دۆزینەوەی کۆد وەستێنرا</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} پێویستە کرابێتە تەختەکلیلی بنەڕەتی بۆ ئەوەی بتوانی کۆدەکە بدۆیتەوە</string>
<string name="settings__theme_editor__code_recording_placeholder">دۆزینەوە…</string>
<string name="settings__theme_editor__add_property">زیادکردنی بەش</string>
<string name="settings__theme_editor__edit_property">دەستکاریکردنی بەش</string>
<string name="settings__theme_editor__property_already_exists">تایبەتمەندیەک بەم ناوە پێشتر لەناو ئەم یاسایەدا هەیە.</string>
@@ -128,7 +135,7 @@
<string name="snygg__rule_element__keyboard">پشتەوەی تەختەکلیل</string>
<string name="snygg__rule_element__key">دوگمە</string>
<string name="snygg__rule_element__key_hint">پیتە لاوەکییەکان</string>
<string name="snygg__rule_element__key_popup">بچووکراوەی پیت</string>
<string name="snygg__rule_element__key_popup">بچووککراوەی پیت</string>
<string name="snygg__rule_element__clipboard_header">سەرەوەی لەبەرگیراوەکان</string>
<string name="snygg__rule_element__clipboard_item">لەبەرگیراوەکان</string>
<string name="snygg__rule_element__clipboard_item_popup">فرمانەکانی لەبەرگیراوەکان</string>
@@ -713,7 +720,7 @@
<!-- Unit strings (symbols) -->
<!-- Unit strings (written words) -->
<plurals name="unit__hours__written">
<item quantity="one">{v} هاتژمێر</item>
<item quantity="one">{v} کاتژمێر</item>
<item quantity="other">{v} کاتژمێر</item>
</plurals>
<plurals name="unit__minutes__written">

View File

@@ -13,6 +13,9 @@
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emoji</string>
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emotikony</string>
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaomoji</string>
<string name="prefs__media__emoji_recently_used_max_size">Maximální velikost historie emotikonů</string>
<string name="prefs__media__emoji_preferred_skin_tone">Preferovaná barva pokožky emotikonů</string>
<string name="prefs__media__emoji_preferred_hair_style">Preferovaný styl vlasů emotikonů</string>
<!-- Emoji strings -->
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Smajlíky &amp; Emoce</string>
<string name="emoji__category__people_body" comment="Emoji category name">Lidé &amp; Tělo</string>
@@ -23,6 +26,9 @@
<string name="emoji__category__objects" comment="Emoji category name">Předměty</string>
<string name="emoji__category__symbols" comment="Emoji category name">Symboly</string>
<string name="emoji__category__flags" comment="Emoji category name">Vlajky</string>
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Nenalezeny žádné nedávno použité emotikony. Jakmile je začnete psát, objeví se automaticky zde.</string>
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Tip: Stiskněte a držte prst na naposledy použitých emotikonech pro jejich odebrání z daného seznamu!</string>
<string name="emoji__recently_used__removal_success_message" comment="Toast message if user has long pressed emoji in recently used collection to remove it">Emotikon {emoji} byl odebrán z nedávno použitých</string>
<!-- Smartbar strings -->
<string name="smartbar__quick_action_toggle__alt" comment="Content description for the quick action toggle button in Smartbar">Přepínač rychlé akce. Pokud ho podržíte, přepne mezi návrhy na slova a tlačítky rychlé akce.</string>
<string name="smartbar__quick_action__exit_editing" comment="Content-description for the exit editing layout button in Smartbar">Opustit editor textu.</string>
@@ -43,13 +49,16 @@
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBord není povolena v systému a proto nemůže být použita jako metoda zadávání. Klikněte sem pro vyřešení tohoto problému.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">FlorisBoard není vybrán jako výchozí vstupní metoda. Kliknutím sem tento problém vyřešíte.</string>
<string name="settings__localization__title" comment="Title of languages and Layout screen">Jazyky a rozložení</string>
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Zobrazovat názvy jazyků v</string>
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">Podtypy</string>
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">Přidat podtyp</string>
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">Upravit podtyp</string>
<string name="settings__localization__subtype_locale" comment="Label for locale dropdown in subtype dialog">Hlavní jazyk</string>
<string name="settings__localization__subtype_popup_mapping" comment="Label for popup mapping dropdown in subtype screen">Mapování vyskakovacích nabídek</string>
<string name="settings__localization__subtype_characters_layout" comment="Label for layout dropdown in subtype dialog">Rozložení znaků</string>
<string name="settings__localization__subtype_symbols_layout" comment="Label for layout dropdown in subtype dialog">Primární rozložení symbolů</string>
<string name="settings__localization__subtype_symbols2_layout" comment="Label for layout dropdown in subtype dialog">Sekundární rozložení symbolů</string>
<string name="settings__localization__subtype_composer" comment="Label for composer dropdown in subtype dialog.">Skládání</string>
<string name="settings__localization__subtype_currency_set" comment="Label for currency set dropdown in subtype dialog. 'set' is used as a noun here and can be compared to a group of elements (in this case currency symbols).">List měn</string>
<string name="settings__localization__subtype_numeric_layout" comment="Label for layout dropdown in subtype dialog">Číselné rozložení</string>
<string name="settings__localization__subtype_numeric_advanced_layout" comment="Label for layout dropdown in subtype dialog">Číselné rozložení (pokročilé)</string>
@@ -66,6 +75,7 @@
<string name="settings__localization__subtype_presets_view_all" comment="View all presets button">Zobrazit všechny</string>
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">Zdá se, že jste nenakonfigurovali žádné podtypy. Jako fallback bude použit podtyp angličtina/QWERTY!</string>
<string name="settings__localization__subtype_error_already_exists" comment="Error message shown in subtype dialog when a subtype to add already exists">Tento podtyp již existuje!</string>
<string name="settings__localization__subtype_error_fields_no_value" comment="Error message shown in subtype editor if at least one field is set to '- select -' (means no value specified)">Minimálně jedno pole nemá vybranou hodnotu. Vyberte ji prosím.</string>
<string name="settings__localization__subtype_error_layout_not_installed" comment="Error message shown in subtype list when a layout is not installed, where %s will be replaced by the layout ID">{layout_id} (nenainstalováno)</string>
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Rozložení</string>
<string name="settings__theme__title" comment="Title of the Theme screen">Motiv</string>
@@ -83,16 +93,16 @@
<string name="settings__theme_manager__title_day" comment="Title of the theme manager screen for day theme selection">Vyberte denní motiv</string>
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">Vyberte noční motiv</string>
<string name="settings__theme_manager__title_manage" comment="Title of the theme manager screen for managing installed and custom themes">Spravovat nainstalované motivy</string>
<string name="settings__theme_editor__fine_tune__title">Jemné doladění editoru</string>
<string name="settings__theme_editor__fine_tune__level">Úprava úrovně</string>
<string name="settings__theme_editor__fine_tune__display_colors_as">Zobrazit barvy jako</string>
<string name="settings__theme_editor__fine_tune__display_kbd_after_dialogs">Zobrazit klávesnici po dialozích</string>
<string name="settings__theme_editor__add_rule">Přidat pravidlo</string>
<string name="settings__theme_editor__edit_rule">Upravit pravidlo</string>
<string name="settings__theme_editor__rule_element">Cílový prvek</string>
<string name="settings__theme_editor__rule_codes">Kódy</string>
<string name="settings__theme_editor__rule_groups">Skupiny</string>
<string name="settings__theme_editor__rule_modes">Režimy</string>
<string name="settings__theme_editor__rule_selectors">Selektory</string>
<string name="settings__theme_editor__add_code">Přidat kód</string>
<string name="settings__theme_editor__edit_code">Upravit kód</string>
<string name="settings__theme_editor__no_codes_defined">Nejsou definovány žádné kódy.</string>
<string name="settings__theme_editor__code_already_exists">Tento klávesový kód je již definován.</string>
<string name="settings__theme_editor__add_property">Přidat vlastnost</string>
<string name="settings__theme_editor__edit_property">Upravit vlastnost</string>
@@ -100,6 +110,7 @@
<string name="settings__theme_editor__property_name">Název vlastnosti</string>
<string name="settings__theme_editor__property_value">Hodnota vlastnosti</string>
<string name="settings__theme_editor__property_value_shape_apply_for_all_corners">Použít pro všechny rohy</string>
<string name="settings__theme_editor__property_value_color_dialog_title">Upravit řetězec barvy</string>
<string name="settings__theme_editor__component_meta_is_night_theme">Noční motiv</string>
<string name="settings__theme_editor__component_meta_is_borderless">Bez okrajů</string>
<string name="snygg__rule_element__defines">Proměnné</string>
@@ -107,25 +118,80 @@
<string name="snygg__rule_element__key">Klávesa</string>
<string name="snygg__rule_element__key_hint">Nápovědy klávesy</string>
<string name="snygg__rule_element__key_popup">Vyskakovací okno klávesy</string>
<string name="snygg__rule_element__clipboard_header">Záhlaví schránky</string>
<string name="snygg__rule_element__clipboard_item">Položka schránky</string>
<string name="snygg__rule_element__clipboard_item_popup">Vyskakovací nabídka položky schránky</string>
<string name="snygg__rule_element__glide_trail">Stopa tahu</string>
<string name="snygg__rule_element__one_handed_panel">Režim jedné ruky</string>
<string name="snygg__rule_element__smartbar_primary_row">Primární řádek chytré lišty</string>
<string name="snygg__rule_element__smartbar_secondary_row">Sekundární řádek chytré lišty</string>
<string name="snygg__rule_element__smartbar_primary_actions_toggle">Primární přepínač akcí chytré lišty</string>
<string name="snygg__rule_element__smartbar_secondary_actions_toggle">Sekundární přepínač akcí rychlé lišty</string>
<string name="snygg__rule_element__smartbar_quick_action">Rychlá akce chytré lišty</string>
<string name="snygg__rule_element__smartbar_key">Klíč chytré lišty</string>
<string name="snygg__rule_element__smartbar_candidate_word">Navrhnuté slovo v chytré liště</string>
<string name="snygg__rule_element__smartbar_candidate_clip">Navrhnutý klip v chytré liště</string>
<string name="snygg__rule_element__smartbar_candidate_spacer">Odsazení navrhnutí v chytré liště</string>
<string name="snygg__rule_element__system_nav_bar">Systémová navigační lišta</string>
<string name="snygg__rule_selector__pressed">Stisknuto</string>
<string name="snygg__rule_selector__focus">Zaměřeno</string>
<string name="snygg__rule_selector__disabled">Zakázáno</string>
<string name="snygg__property_name__width">Šířka</string>
<string name="snygg__property_name__height">Výška</string>
<string name="snygg__property_name__background">Pozadí</string>
<string name="snygg__property_name__foreground">Popředí</string>
<string name="snygg__property_name__border_color">Barva okraje</string>
<string name="snygg__property_name__border_style">Styl okraje</string>
<string name="snygg__property_name__border_width">Šířka okraje</string>
<string name="snygg__property_name__font_family">Rodina písem</string>
<string name="snygg__property_name__font_size">Velikost písma</string>
<string name="snygg__property_name__font_style">Styl písma</string>
<string name="pref__input_feedback__group_haptic__label" comment="Preference group title">Hmatová odezva / vibrace</string>
<string name="pref__input_feedback__haptic_enabled__label" comment="Preference title">Povolit hmatovou odezvu</string>
<string name="pref__input_feedback__haptic_enabled__summary" comment="Preference summary">Vibrovat při vstupních událostech, záleží na nastavení systému</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__label" comment="Preference title">Ignorovat hmatová nastavení systému</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__summary" comment="Preference summary">Použít předvolby níže, i při vypnutých vybracích v systému</string>
<string name="pref__input_feedback__haptic_use_vibrator__label" comment="Preference title">Přímé spuštění vibrací</string>
<string name="pref__input_feedback__haptic_use_vibrator__summary" comment="Preference summary">Spustit vibrace přímo namísto použití sady pro hmatovou odezvu systému Android</string>
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">Trvání vibrací</string>
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">Síla vibrací</string>
<string name="pref__input_feedback__haptic_feat_key_press__label" comment="Preference title">Vibrace při stisku klávesy</string>
<string name="pref__input_feedback__haptic_feat_key_long_press__label" comment="Preference title">Vibrace při dlouhém stisku klávesy</string>
<string name="pref__input_feedback__haptic_feat_key_repeated_action__label" comment="Preference title">Vibrace při opakované akci klávesy</string>
<string name="pref__input_feedback__haptic_feat_gesture_swipe__label" comment="Preference title">Vibrace při gestech</string>
<string name="pref__input_feedback__haptic_feat_gesture_moving_swipe__label" comment="Preference title">Vibrace při posouvání prstem při gestech</string>
<string name="pref__input_feedback__any_feat_key_press__summary" comment="Preference summary">např. klávesy, tlačítka, záložky emotikonů</string>
<string name="pref__input_feedback__any_feat_key_long_press__summary" comment="Preference summary">např. vyskakovací nabídky</string>
<string name="pref__input_feedback__any_feat_key_repeated_action__summary" comment="Preference summary">např. klávesa odstranění</string>
<string name="pref__input_feedback__any_feat_gesture_swipe__summary" comment="Preference summary">není implementováno</string>
<string name="pref__input_feedback__any_feat_gesture_moving_swipe__summary" comment="Preference summary">např. posunutí při ovládání kurzoru</string>
<string name="settings__keyboard__title" comment="Title of Keyboard preferences screen">Klávesnice</string>
<string name="pref__keyboard__number_row__label" comment="Preference title">Řádek čísel</string>
<string name="pref__keyboard__number_row__summary" comment="Preference summary">Zobrazit číselný řádek nad znakovým rozložením</string>
<string name="pref__keyboard__hinted_number_row_mode__label" comment="Preference title">Řádek čísel s návrhy</string>
<string name="pref__keyboard__hinted_symbols_mode__label" comment="Preference title">Navrhované symboly</string>
<string name="pref__keyboard__utility_key_enabled__label" comment="Preference title">Zobrazit pomocné tlačítko</string>
<string name="pref__keyboard__utility_key_enabled__summary" comment="Preference summary">Zobrazí nastavitelné pomocné tlačítko vedle mezerníku</string>
<string name="pref__keyboard__utility_key_action__label" comment="Preference title">Funkce pomocného tlačítka</string>
<string name="pref__keyboard__space_bar_language_display_enabled__label" comment="Preference title">Zobrazit jazyk na mezerníku</string>
<string name="pref__keyboard__space_bar_language_display_enabled__summary" comment="Preference summary">Zobrazí název aktuálně používaného jazyka na mezerníku</string>
<string name="pref__keyboard__font_size_multiplier__label" comment="Preference title">Násobitel velikosti písma</string>
<string name="pref__keyboard__group_layout__label" comment="Preference group title">Rozložení</string>
<string name="pref__keyboard__one_handed_mode__label" comment="Preference title">Režim jedné ruky</string>
<string name="pref__keyboard__one_handed_mode_scale_factor__label" comment="Preference title">Šířka klávesnice v režimu jedné ruky</string>
<string name="pref__keyboard__landscape_input_ui_mode__label" comment="Preference value">Vstupní okno v celoobrazovkovém režimu na šířku</string>
<string name="pref__keyboard__height_factor__label" comment="Preference title">Výška klávesnice</string>
<string name="pref__keyboard__key_spacing__label" comment="Preference title">Mezery mezi klávesami</string>
<string name="pref__keyboard__bottom_offset__label" comment="Preference title">Spodní odsazení</string>
<string name="pref__keyboard__group_keypress__label" comment="Preference group title">Stisk klávesy</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">Viditelnost vyskakovacího okna</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">Zobrazit vyskakovací okno při stisku klávesy</string>
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">Délka dlouhého stisku</string>
<!-- Smartbar strings -->
<string name="settings__smartbar__title" comment="Title of Smartbar screen">Chytrá lišta</string>
<string name="pref__smartbar__enabled__label" comment="Preference title">Povolit chytrou lištu</string>
<string name="pref__smartbar__enabled__summary" comment="Preference summary">Zobrazí se v horní části klávesnice</string>
<string name="pref__smartbar__flip_toggles__label" comment="Preference title">Přehodit přepínací tlačítka</string>
<!-- Typing strings -->
<string name="pref__suggestion__title" comment="Preference group title">Návrhy</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">Návrhy obsahu schránky</string>

View File

@@ -103,16 +103,18 @@
<string name="settings__theme_editor__no_rules_defined">Für das Stylesheet sind keine Regeln definiert. Füge eine Regel hinzu, um dieses Stylesheet anzupassen.</string>
<string name="settings__theme_editor__rule_already_exists">Diese Stylesheetregel ist schon definiert.</string>
<string name="settings__theme_editor__rule_element">Zielelement</string>
<string name="settings__theme_editor__rule_codes">Codes</string>
<string name="settings__theme_editor__rule_groups">Gruppen</string>
<string name="settings__theme_editor__rule_modes">Modi</string>
<string name="settings__theme_editor__rule_selectors">Selektoren</string>
<string name="settings__theme_editor__add_code">Code hinzufügen</string>
<string name="settings__theme_editor__edit_code">Code bearbeiten</string>
<string name="settings__theme_editor__no_codes_defined">Keine Codes definiert.</string>
<string name="settings__theme_editor__no_codes_defined">Regel auf alle Zielelemente anwenden.</string>
<string name="settings__theme_editor__codes_defined">Regel nur auf Zielelemente mit folgenden Codes anwenden:</string>
<string name="settings__theme_editor__code_already_exists">Dieser Tastencode ist bereits definiert.</string>
<string name="settings__theme_editor__code_invalid">Dieser Tastencode ist nicht gültig. Stelle sicher, dass der Tastencode innerhalb der Spanne von {c_min} bis {c_max} für Zeichen oder von {i_min} bis {i_max} für interne Spezialschlüssel liegt.</string>
<string name="settings__theme_editor__code_help_text">Die folgenden Links helfen dir, den entsprechenden Tastencode zu finden:</string>
<string name="settings__theme_editor__code_help_text">Alternativ werden die folgenden Links helfen die korrespondierenden Codes zu finden:</string>
<string name="settings__theme_editor__code_placeholder">Code</string>
<string name="settings__theme_editor__code_recording_help_text">Um den Code einer Taste zu ermitteln, verwenden Sie die Schaltfläche neben dem Code-Eingabefeld. Sobald sie aktiviert ist, zeichnet sie den nächsten Tastendruck auf und fügt den Code in das Eingabefeld ein.</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} muss die standard Tastatur-App sein, um eine Taste aufzunehmen</string>
<string name="settings__theme_editor__code_recording_placeholder">Aufnahme läuft…</string>
<string name="settings__theme_editor__add_property">Eigenschaft hinzufügen</string>
<string name="settings__theme_editor__edit_property">Eigenschaft bearbeiten</string>
<string name="settings__theme_editor__property_already_exists">Eine Eigenschaft mit diesem Namen existiert bereits in der aktuellen Regel.</string>
@@ -136,6 +138,8 @@
<string name="snygg__rule_element__one_handed_panel">Einhändiges Feld</string>
<string name="snygg__rule_element__smartbar_primary_row">Smartbar Primärleiste</string>
<string name="snygg__rule_element__smartbar_secondary_row">Smartbar Sekundärleiste</string>
<string name="snygg__rule_element__smartbar_primary_actions_toggle">Smartbar primäre Aktionszeile Schalter</string>
<string name="snygg__rule_element__smartbar_quick_action">Smartbar Schnellzugriff</string>
<string name="snygg__rule_element__smartbar_key">Smartbartaste</string>
<string name="snygg__rule_element__smartbar_candidate_word">Smartbar Wortkandidat</string>
<string name="snygg__rule_element__smartbar_candidate_clip">Smartbar Kandidatenclip</string>
@@ -240,14 +244,22 @@
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">Pop-Up Sichtbarkeit</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">Pop-Up bei Tastendruck anzeigen</string>
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">Verzögerung bei langem Tastendruck</string>
<string name="pref__keyboard__space_bar_switches_to_characters__label" comment="Preference title">Leertaste wechselt zu Buchstaben zurück</string>
<string name="pref__keyboard__space_bar_switches_to_characters__summary" comment="Preference summary">Leertaste wechselt aus der Symbol- oder Zahlenansicht zurück zur Buchstabenansicht</string>
<!-- Smartbar strings -->
<string name="settings__smartbar__title" comment="Title of Smartbar screen">Schnellzugriffsleiste</string>
<string name="pref__smartbar__enabled__label" comment="Preference title">Schnellzugriffsleiste einschalten</string>
<string name="pref__smartbar__enabled__summary" comment="Preference summary">Wird über der Tastatur angezeigt</string>
<string name="pref__smartbar__flip_toggles__label" comment="Preference title">Tausche Schalter für Zugriffsleisten</string>
<string name="pref__smartbar__any_row_type__label" comment="Preference title">Reihenlayout</string>
<string name="pref__smartbar__group_primary_actions__label" comment="Preference group title">Primäre Aktion</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__label" comment="Preference title">Automatisch aus-/einklappen</string>
<string name="pref__smartbar__group_secondary_actions__label" comment="Preference group title">Sekundäre Aktionen</string>
<string name="pref__smartbar__secondary_actions_enabled__label" comment="Preference title">Zweite Navigationsleiste anzeigen</string>
<!-- Typing strings -->
<string name="settings__typing__title" comment="Title of Typing experience screen">Vorschläge &amp; Korrekturen</string>
<string name="pref__suggestion__title" comment="Preference group title">Vorschläge</string>
<string name="pref__suggestion__api30_inline_suggestions_enabled__summary" comment="Preference summary">Zeige Inline-Vorschläge von Autofill-Diensten</string>
<string name="pref__suggestion__display_mode__label" comment="Preference title">Darstellungsart für Vorschläge</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">Inhalt der Zwischenablage</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">Timeout für Vorschläge aus der Zwischenablage</string>
@@ -647,6 +659,7 @@
<string name="enum__shape_corner__top_end" comment="Enum value label">Ende oben</string>
<string name="enum__shape_corner__bottom_end" comment="Enum value label">Ende unten</string>
<string name="enum__shape_corner__bottom_start" comment="Enum value label">Anfang unten</string>
<string name="enum__smartbar_row_type__quick_actions" comment="Enum value label">Schnellzugriffe</string>
<string name="enum__snygg_level__basic" comment="Enum value label">Einfach</string>
<string name="enum__snygg_level__basic__description" comment="Enum value description">Nur Farbeigenschaften werden angezeigt. Eigenschaften und Regeln werden übersetzt.</string>
<string name="enum__snygg_level__advanced" comment="Enum value label">Erweitert</string>

View File

@@ -27,6 +27,7 @@
<string name="emoji__category__symbols" comment="Emoji category name">Símbolos</string>
<string name="emoji__category__flags" comment="Emoji category name">Banderas</string>
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">No se han encontrado emojis que se hayan usado recientemente. Una vez empieces a usar emojis, aparecerán automáticamente aquí.</string>
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Consejo: ¡Pulse sostenidamente los emojis usados recientemente para poder removerlos de esta vista!</string>
<string name="emoji__recently_used__removal_success_message" comment="Toast message if user has long pressed emoji in recently used collection to remove it">Se ha removido {emoji} de los emojis recientes</string>
<!-- Smartbar strings -->
<string name="smartbar__quick_action_toggle__alt" comment="Content description for the quick action toggle button in Smartbar">Cambio de acción rápida. Si se pulsa, cambia entre las sugerencias de palabras y los botones de acción rápida.</string>
@@ -99,14 +100,22 @@
<string name="settings__theme_editor__fine_tune__display_kbd_after_dialogs">Mostrar teclado después de diálogos</string>
<string name="settings__theme_editor__add_rule">Añadir regla</string>
<string name="settings__theme_editor__edit_rule">Editar regla</string>
<string name="settings__theme_editor__no_rules_defined">Esta hoja de estilo no tiene reglas definidas. Agregue una regla para comenzar a personalizar esta hoja de estilo.</string>
<string name="settings__theme_editor__rule_already_exists">Esta regla de hoja de estilo ya está definida.</string>
<string name="settings__theme_editor__rule_element">Elemento destino</string>
<string name="settings__theme_editor__rule_codes">Códigos</string>
<string name="settings__theme_editor__rule_codes">Códigos de clave destino</string>
<string name="settings__theme_editor__rule_groups">Grupos</string>
<string name="settings__theme_editor__rule_modes">Modos</string>
<string name="settings__theme_editor__rule_selectors">Selectores</string>
<string name="settings__theme_editor__add_code">Añadir código</string>
<string name="settings__theme_editor__edit_code">Editar código</string>
<string name="settings__theme_editor__no_codes_defined">No hay códigos definidos.</string>
<string name="settings__theme_editor__add_code">Añadir código clave</string>
<string name="settings__theme_editor__edit_code">Editar código clave</string>
<string name="settings__theme_editor__no_codes_defined">Aplicar la regla a todos los elementos de destino.</string>
<string name="settings__theme_editor__codes_defined">Aplicar regla solo para desviar los elementos con los siguientes códigos clave:</string>
<string name="settings__theme_editor__code_already_exists">Este código clave ya está definido.</string>
<string name="settings__theme_editor__code_invalid">Este código clave no es válido. Asegúrese de que el código clave esté dentro del rango de {c_min} a {c_max} para los caracteres o {i_min} a {i_max} para las teclas especiales internas.</string>
<string name="settings__theme_editor__code_help_text">Alternativamente, los siguientes enlaces lo ayudarán a encontrar el código clave correspondiente:</string>
<string name="settings__theme_editor__code_placeholder">Código</string>
<string name="settings__theme_editor__code_recording_placeholder">Grabando…</string>
<string name="settings__theme_editor__add_property">Añadir propiedad</string>
<string name="settings__theme_editor__edit_property">Editar propiedad</string>
<string name="settings__theme_editor__property_already_exists">Ya existe una propiedad con este nombre dentro de la regla actual.</string>
@@ -128,6 +137,8 @@
<string name="snygg__rule_element__one_handed_panel">Panel a una mano</string>
<string name="snygg__rule_element__smartbar_primary_row">Fila principal de barra inteligente</string>
<string name="snygg__rule_element__smartbar_secondary_row">Fila secundaria de barra inteligente</string>
<string name="snygg__rule_element__smartbar_primary_actions_toggle">Acciones primarias de la barra inteligente</string>
<string name="snygg__rule_element__smartbar_secondary_actions_toggle">Acciones secundarias de la barra inteligente</string>
<string name="snygg__rule_element__smartbar_quick_action">Barra inteligente de acciones rápidas</string>
<string name="snygg__rule_element__smartbar_key">Tecla de barra inteligente</string>
<string name="snygg__rule_element__smartbar_candidate_word">Palabra candidata de barra inteligente</string>

View File

@@ -9,9 +9,13 @@
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">جابه‌جایی صفحه کلید به سمت چپ.</string>
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">جابه‌جایی صفحه کلید به سمت راست.</string>
<!-- Media strings -->
<string name="settings__media__title">ایموجی ها</string>
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">شکلک‌ها</string>
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emoticons</string>
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaomoji</string>
<string name="prefs__media__emoji_recently_used_max_size">حداکثر حجم تاریخچه ایموجی</string>
<string name="prefs__media__emoji_preferred_skin_tone">رنگ پوست پیشفرض برای ایموجی</string>
<string name="prefs__media__emoji_preferred_hair_style">مدل مو پیشفرض برای ایموجی</string>
<!-- Emoji strings -->
<string name="emoji__category__smileys_emotion" comment="Emoji category name">لبخندی &amp; احساسات</string>
<string name="emoji__category__people_body" comment="Emoji category name">مردم &amp; بدن</string>
@@ -22,6 +26,9 @@
<string name="emoji__category__objects" comment="Emoji category name">اشیاء</string>
<string name="emoji__category__symbols" comment="Emoji category name">نمادها</string>
<string name="emoji__category__flags" comment="Emoji category name">پرچم ها</string>
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">ایموجی استفاده شده ای یافت نشد. زمانی که شروع به نوشتن ایموجی ها بکنید به صورت خودکار اینجا ظاهر خواهند شد.</string>
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">توصیه کاربردی: روی ایموجی های استفاده شده نگهدارید تا آن ها را از اینجا حذف کنید!</string>
<string name="emoji__recently_used__removal_success_message" comment="Toast message if user has long pressed emoji in recently used collection to remove it">ایموجی {emoji} از ایموجی های اخیرا استفاده شده حذف شد</string>
<!-- Smartbar strings -->
<string name="smartbar__quick_action_toggle__alt" comment="Content description for the quick action toggle button in Smartbar">ضامن اقدام سریع. در صورت فشار ، بین پیشنهادات کلمه و دکمه های اقدام سریع جابجا می شود.</string>
<string name="smartbar__quick_action__exit_editing" comment="Content-description for the exit editing layout button in Smartbar">خروج از صفحه ویرایش متن.</string>
@@ -34,6 +41,7 @@
<string name="smartbar__quick_action__private_mode" comment="Content-description for the private mode button in Smartbar">در ضورت قابل مشاهده بودن، نشان میـدهد که حالت خصوصی فعال است. در صورت ضربه روی ان، اطلاعاتی درباره حالت خصوصی نشان می‌دهد.</string>
<!-- Settings UI strings -->
<string name="settings__title" comment="Title of Settings">تنظیمات</string>
<string name="settings__preview_keyboard" comment="Hint for try your setup box">کیبوردتان را امتحان کنید</string>
<string name="settings__help" comment="General label for help buttons in Settings">راهنمایی</string>
<string name="settings__default" comment="General string which is used when a preference has the default value set">پیشفرض</string>
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">پیشفرض سیستم</string>
@@ -41,10 +49,12 @@
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">فلوریس بورد در سیستم به عنوان روش ورودی فعلا نیست و به این علت به عنوان روش ورودی قابل انتخاب نیست. برای حل این مشکل اینجا کلیک کنید.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">فلوریس بود به عنوان روش ورودی پیش فرض انتخاب نشده. برای حل این مشکل اینجا کلیک کنید.</string>
<string name="settings__localization__title" comment="Title of languages and Layout screen">زبان ها &amp; چیدمان ها</string>
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">نام زبان ها را به این صورت نمایش بده</string>
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">زیرنوع ها</string>
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">افزودن زیر-نوعی</string>
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">ویرایش زیر-نوعی</string>
<string name="settings__localization__subtype_locale" comment="Label for locale dropdown in subtype dialog">زبان اصلی</string>
<string name="settings__localization__subtype_popup_mapping" comment="Label for popup mapping dropdown in subtype screen">چینش پاپ آپ ها</string>
<string name="settings__localization__subtype_characters_layout" comment="Label for layout dropdown in subtype dialog">چیدمان کاراکتر ها</string>
<string name="settings__localization__subtype_symbols_layout" comment="Label for layout dropdown in subtype dialog">چیدمان اصلی نشانه ها</string>
<string name="settings__localization__subtype_symbols2_layout" comment="Label for layout dropdown in subtype dialog">چیدمان فرعی نشانه ها</string>
@@ -63,6 +73,7 @@
<string name="settings__localization__subtype_presets_view_all" comment="View all presets button">نمایش همه</string>
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">به نظر میرسد که هیچگونه زیرگروه دیگری را انتخاب نکرده اید. به همین علت زیرگروه English/QWERTY استفاده خواهد شد!</string>
<string name="settings__localization__subtype_error_already_exists" comment="Error message shown in subtype dialog when a subtype to add already exists">این زیرنوع درحال حاضر وجود دارد!</string>
<string name="settings__localization__subtype_error_fields_no_value" comment="Error message shown in subtype editor if at least one field is set to '- select -' (means no value specified)">حداقل یکی از فیلد ها خالی است. لطفا یک گزینه برای فیلد(ها) انتخاب کنید.</string>
<string name="settings__localization__subtype_error_layout_not_installed" comment="Error message shown in subtype list when a layout is not installed, where %s will be replaced by the layout ID">{layout_id} (نصب نشده)</string>
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">چیدمان ها</string>
<string name="settings__theme__title" comment="Title of the Theme screen">تم</string>
@@ -77,6 +88,40 @@
<string name="pref__theme__source_assets" comment="Label for the theme source field">ابزار های برنامه فلوریس بورد</string>
<string name="pref__theme__source_internal" comment="Label for the theme source field">حافظه داخلی</string>
<string name="pref__theme__source_external" comment="Label for the theme source field">ارائه دهنده خارجی</string>
<string name="settings__theme_manager__title_day" comment="Title of the theme manager screen for day theme selection">انتخاب تم روز</string>
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">انتخاب تم شب</string>
<string name="settings__theme_manager__title_manage" comment="Title of the theme manager screen for managing installed and custom themes">مدیریت تم های نصب شده</string>
<string name="settings__theme_editor__fine_tune__level">سطح ویرایشگر</string>
<string name="settings__theme_editor__fine_tune__display_colors_as">رنگ را مانند ... نشان بده</string>
<string name="settings__theme_editor__add_rule">افزودن قانون</string>
<string name="settings__theme_editor__edit_rule">ویرایش قانون</string>
<string name="settings__theme_editor__no_rules_defined">این شیوه نامه قانون تعریف شده ای ندارد. یک قانون اضافه کنید تا این شیوه نامه را شخصی سازی کنید.</string>
<string name="settings__theme_editor__rule_already_exists">این قانون شیوه نامه از قبل تعریف شده است.</string>
<string name="settings__theme_editor__rule_element">مؤلفه مورد نظر</string>
<string name="settings__theme_editor__rule_codes">کد کلیدهای مورد نظر</string>
<string name="settings__theme_editor__rule_groups">گروه ها</string>
<string name="settings__theme_editor__rule_modes">حالت ها</string>
<string name="settings__theme_editor__rule_selectors">انتخابگرها</string>
<string name="settings__theme_editor__add_code">افزودن کد کلید</string>
<string name="settings__theme_editor__edit_code">ویرایش کد کلید</string>
<string name="settings__theme_editor__no_codes_defined">قانون را برای تمام مؤلفه ها اعمال کن.</string>
<string name="settings__theme_editor__codes_defined">قانون را فقط برای مؤلفه های همراه با این کد کلیدها اعمال کن:</string>
<string name="settings__theme_editor__code_already_exists">این کد کلید از قبل تعریف شده است.</string>
<string name="settings__theme_editor__code_invalid">این کد کلید صحیح نیست. مطمئن شوید که کد کلید برای کاراکتر ها بین {c_min} تا {c_max} یا برای کلیدهای ویژه داخلی بین {i_min} تا {i_max} هست.</string>
<string name="settings__theme_editor__code_help_text">در ضمن لینک های زیر به شما برای یافتن کد کلید متناظر کمک خواهند کرد:</string>
<string name="settings__theme_editor__code_placeholder">کد</string>
<string name="settings__theme_editor__code_recording_help_text">برای یافتن کد یک کلید، از دکمه کنار فیلد ورودی کد استفاده کنید. وقتی که فعال شود، کلید فعال شده بعدی را ضبط کرده و کد آن را وارد فیلد ورودی می کند.</string>
<string name="settings__theme_editor__code_recording_started">ضبط کد کلید شروع شد</string>
<string name="settings__theme_editor__code_recording_stopped">ضبط کد کلید پایان یافت</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} باید به عنوان کیبورد پیشفرض انتخاب شود تا کد کلید را بتوان ضبط کرد</string>
<string name="settings__theme_editor__code_recording_placeholder">در حال ضبط...</string>
<string name="settings__theme_editor__add_property">افزودن ویژگی</string>
<string name="settings__theme_editor__edit_property">ویرایش ویژگی</string>
<string name="settings__theme_editor__property_already_exists">یک ویزگی با همین نام در این قانون از قبل وجود دارد.</string>
<string name="settings__theme_editor__property_name">نام ویژگی</string>
<string name="settings__theme_editor__property_value">مقدار ویژگی</string>
<string name="settings__theme_editor__property_value_shape_apply_for_all_corners">برای تمام گوشه ها اعمال کن</string>
<string name="settings__theme_editor__property_value_color_dialog_title">ویرایش متن رنگ</string>
<string name="settings__input_feedback__title" comment="Title of Input Feedback screen">صداها &amp; لرزش</string>
<string name="pref__input_feedback__group_audio__label" comment="Preference group title">صدای بازخورد / صداها</string>
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">فعالسازی صدای بازخورد</string>

View File

@@ -103,16 +103,23 @@
<string name="settings__theme_editor__no_rules_defined">Cette feuille de style n\'a pas de règles définies. Ajoutez une règle pour commencer à personnaliser cette feuille de style.</string>
<string name="settings__theme_editor__rule_already_exists">Cette règle de feuille de style est déjà définie.</string>
<string name="settings__theme_editor__rule_element">Élément cible</string>
<string name="settings__theme_editor__rule_codes">Codes</string>
<string name="settings__theme_editor__rule_codes">Codes clés de la cible</string>
<string name="settings__theme_editor__rule_groups">Groupes</string>
<string name="settings__theme_editor__rule_modes">Modes</string>
<string name="settings__theme_editor__rule_selectors">Sélecteurs</string>
<string name="settings__theme_editor__add_code">Ajouter du code</string>
<string name="settings__theme_editor__edit_code">Modifier le code</string>
<string name="settings__theme_editor__no_codes_defined">Aucun code défini.</string>
<string name="settings__theme_editor__add_code">Ajouter un code clé</string>
<string name="settings__theme_editor__edit_code">Modifier le code de la clé</string>
<string name="settings__theme_editor__no_codes_defined">Appliquer la règle à tous les éléments cibles.</string>
<string name="settings__theme_editor__codes_defined">Appliquer la règle uniquement aux éléments cibles avec les codes clés suivants :</string>
<string name="settings__theme_editor__code_already_exists">Ce code clé est déjà défini.</string>
<string name="settings__theme_editor__code_invalid">Ce code clé n\'est pas valide. Assurez-vous que le code de clé est dans la plage de {c_min} à {c_max} pour les caractères ou de {i_min} à {i_max} pour les clés spéciales internes.</string>
<string name="settings__theme_editor__code_help_text">Les liens suivants vous aideront à trouver le code clé correspondant :</string>
<string name="settings__theme_editor__code_help_text">Sinon, les liens suivants vous aideront à trouver le code clé correspondant :</string>
<string name="settings__theme_editor__code_placeholder">Code</string>
<string name="settings__theme_editor__code_recording_help_text">Pour trouver le code d\'une touche, utilisez le bouton situé à côté du champ de saisie du code. Une fois activé, il enregistrera la prochaine pression sur la touche et insérera le code dans le champ de saisie.</string>
<string name="settings__theme_editor__code_recording_started">L\'enregistrement du code clé a commencé</string>
<string name="settings__theme_editor__code_recording_stopped">L\'enregistrement du code clé s\'est arrêté</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} doit être le clavier par défaut pour enregistrer un code de touche</string>
<string name="settings__theme_editor__code_recording_placeholder">Enregistrement…</string>
<string name="settings__theme_editor__add_property">Ajouter une propriété</string>
<string name="settings__theme_editor__edit_property">Modifier la propriété</string>
<string name="settings__theme_editor__property_already_exists">Une propriété portant ce nom existe déjà dans la règle actuelle.</string>
@@ -127,6 +134,7 @@
<string name="snygg__rule_element__defines_description">Définissez des variables dans cette règle pour réutiliser des couleurs ou des tailles courantes dans votre feuille de style.</string>
<string name="snygg__rule_element__keyboard">Fenêtre du clavier</string>
<string name="snygg__rule_element__key">Touche</string>
<string name="snygg__rule_element__key_hint">Indice clé</string>
<string name="snygg__rule_element__key_popup">Pop-up de touche</string>
<string name="snygg__rule_element__clipboard_header">En-tête du presse-papiers</string>
<string name="snygg__rule_element__clipboard_item">Élément du Presse-papiers</string>
@@ -135,6 +143,8 @@
<string name="snygg__rule_element__one_handed_panel">Panneau à une main</string>
<string name="snygg__rule_element__smartbar_primary_row">Rangée principale de la barre intelligente</string>
<string name="snygg__rule_element__smartbar_secondary_row">Rangée secondaire de la barre intelligente</string>
<string name="snygg__rule_element__smartbar_primary_actions_toggle">Activation des actions primaires de la barre intelligente</string>
<string name="snygg__rule_element__smartbar_secondary_actions_toggle">Activation des actions secondaires de la barre intelligente</string>
<string name="snygg__rule_element__smartbar_quick_action">Action rapides de la barre intelligente</string>
<string name="snygg__rule_element__smartbar_key">Touche de barre intelligente</string>
<string name="snygg__rule_element__smartbar_candidate_word">Mot candidat de la barre intelligente</string>
@@ -182,6 +192,7 @@
<string name="snygg__property_value__dp_size">Taille (dp)</string>
<string name="snygg__property_value__sp_size">Taille (sp)</string>
<string name="snygg__property_value__percentage_size">Taille (%)</string>
<string name="snygg__property_value__defined_var">Référence du Var</string>
<string name="settings__input_feedback__title" comment="Title of Input Feedback screen">Sons &amp; Vibration</string>
<string name="pref__input_feedback__group_audio__label" comment="Preference group title">Retour audio / Sons</string>
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">Activer le retour audio</string>
@@ -236,6 +247,8 @@
<string name="pref__keyboard__group_keypress__label" comment="Preference group title">Appui sur la touche</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">Visibilité du popup</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">Afficher un pop-up lors de l\'appui d\'une touche</string>
<string name="pref__keyboard__merge_hint_popups_enabled__label" comment="Preference title">Les accents comprennent des popups de symboles</string>
<string name="pref__keyboard__merge_hint_popups_enabled__summary" comment="Preference summary">Ajoute des popups de symboles aux accents de la mise en page par défaut</string>
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">Délai d\'appui prolongé</string>
<string name="pref__keyboard__space_bar_switches_to_characters__label" comment="Preference title">Espace pour revenir aux caractères</string>
<string name="pref__keyboard__space_bar_switches_to_characters__summary" comment="Preference summary">Retour automatique aux caractères depuis les symboles et les nombres</string>
@@ -243,8 +256,12 @@
<string name="settings__smartbar__title" comment="Title of Smartbar screen">Barre intelligente</string>
<string name="pref__smartbar__enabled__label" comment="Preference title">Activer la Barre Intelligente</string>
<string name="pref__smartbar__enabled__summary" comment="Preference summary">Sera affiché au dessus du clavier</string>
<string name="pref__smartbar__flip_toggles__label" comment="Preference title">Boutons à bascule</string>
<string name="pref__smartbar__flip_toggles__summary" comment="Preference summary">La rangée d\'action bascule</string>
<string name="pref__smartbar__any_row_type__label" comment="Preference title">Disposition des rangs</string>
<string name="pref__smartbar__group_primary_actions__label" comment="Preference group title">Actions principales</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__label" comment="Preference title">Agrandir/réduire automatiquement</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__summary" comment="Preference summary">Extension/réduction automatique de la ligne d\'action primaire en fonction de l\'état actuel</string>
<string name="pref__smartbar__group_secondary_actions__label" comment="Preference group title">Actions secondaires</string>
<string name="pref__smartbar__secondary_actions_enabled__label" comment="Preference title">Afficher la ligne d\'actions secondaires</string>
<!-- Typing strings -->
@@ -647,7 +664,14 @@
<string name="enum__secondary_row_placement__below_primary__description" comment="Enum value description">Placer la ligne secondaire entre la ligne principale et le clavier de texte</string>
<string name="enum__secondary_row_placement__overlay_app_ui" comment="Enum value label">Superposer l\'interface utilisateur de l\'application</string>
<string name="enum__secondary_row_placement__overlay_app_ui__description" comment="Enum value description">Place la ligne secondaire en superposition au-dessus de l\'interface utilisateur de l\'application, sans affecter la hauteur résultante de l\'interface utilisateur du clavier. Notez que ce placement peut entraîner un dépassement partiel du champ de saisie de l\'application</string>
<string name="enum__shape_corner__top_start" comment="Enum value label">Top départ</string>
<string name="enum__shape_corner__top_end" comment="Enum value label">Top fin</string>
<string name="enum__shape_corner__bottom_end" comment="Enum value label">Extrémité inférieure</string>
<string name="enum__shape_corner__bottom_start" comment="Enum value label">Extrémité supérieure</string>
<string name="enum__smartbar_row_type__quick_actions" comment="Enum value label">Actions rapides</string>
<string name="enum__smartbar_row_type__quick_actions__description" comment="Enum value description">Actions rapides pouvant être organisées sous forme de rangées avec un menu de débordement</string>
<string name="enum__smartbar_row_type__clipboard_cursor_tools" comment="Enum value label">Outils du curseur du presse-papiers</string>
<string name="enum__smartbar_row_type__clipboard_cursor_tools__description" comment="Enum value description">Rame avec les outils les plus couramment utilisés pour le presse-papiers et le déplacement du curseur</string>
<string name="enum__snygg_level__basic" comment="Enum value label">Standard</string>
<string name="enum__snygg_level__basic__description" comment="Enum value description">Seules les propriétés de couleur sont affichées, les propriétés et les règles sont traduites.</string>
<string name="enum__snygg_level__advanced" comment="Enum value label">Avancé</string>

View File

@@ -93,13 +93,9 @@
<string name="settings__theme_editor__add_rule">Szabály hozzáadása</string>
<string name="settings__theme_editor__edit_rule">Szabály szerkesztése</string>
<string name="settings__theme_editor__rule_element">Cél elem</string>
<string name="settings__theme_editor__rule_codes">Kódok</string>
<string name="settings__theme_editor__rule_groups">Csoportok</string>
<string name="settings__theme_editor__rule_modes">Módok</string>
<string name="settings__theme_editor__rule_selectors">Választók</string>
<string name="settings__theme_editor__add_code">Kód hozzáadása</string>
<string name="settings__theme_editor__edit_code">Kód szerkesztése</string>
<string name="settings__theme_editor__no_codes_defined">Nincs kód definiálva.</string>
<string name="settings__theme_editor__add_property">Tulajdonság hozzáadása</string>
<string name="settings__theme_editor__edit_property">Tulajdonság szerkesztése</string>
<string name="settings__theme_editor__property_already_exists">A jelenlegi szabályon belül egy tulajdonság már létezik ezzel a névvel.</string>
@@ -149,11 +145,19 @@
<string name="snygg__property_value__defined_var">Változóhivatkozás</string>
<string name="settings__input_feedback__title" comment="Title of Input Feedback screen">Hangok és rezgés</string>
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">Hangvisszajelzés engedélyezése</string>
<string name="pref__input_feedback__audio_ignore_system_settings__label" comment="Preference title">A rendszer hangbeállításainka figyelmen kívül hagyása</string>
<string name="pref__input_feedback__audio_ignore_system_settings__label" comment="Preference title">A rendszer hangbeállításainak figyelmen kívül hagyása</string>
<string name="pref__input_feedback__audio_ignore_system_settings__summary" comment="Preference summary">Az alábbi beállítások használata, akkor is ha a hang le van tiltva a rendszerben. Nem működik, ha a rendszer csengőhang le van némítva</string>
<string name="pref__input_feedback__audio_feat_key_press__label" comment="Preference title">Gombnyomáshangok</string>
<string name="pref__input_feedback__group_haptic__label" comment="Preference group title">Haptikus visszajelzés / vibráció</string>
<string name="pref__input_feedback__haptic_enabled__label" comment="Preference title">Haptikus visszajelzés engedélyezése</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__label" comment="Preference title">A rendszer hangbeállításainak figyelmen kívül hagyása</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__summary" comment="Preference summary">Az alábbi beállítások használata, akkor is ha a haptika le van tiltva a rendszerben</string>
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">Rezgés időtartama</string>
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">Rezgés erőssége</string>
<string name="pref__input_feedback__haptic_feat_key_press__label" comment="Preference title">Gombnyomás rezgése</string>
<string name="pref__input_feedback__haptic_feat_key_long_press__label" comment="Preference title">Hosszú gombnyomás rezgése</string>
<string name="pref__input_feedback__any_feat_key_repeated_action__summary" comment="Preference summary">pl. törlés gomb</string>
<string name="pref__input_feedback__any_feat_gesture_swipe__summary" comment="Preference summary">nincs implementálva</string>
<string name="settings__keyboard__title" comment="Title of Keyboard preferences screen">Billentyűzet</string>
<string name="pref__keyboard__number_row__label" comment="Preference title">Számsor</string>
<string name="pref__keyboard__number_row__summary" comment="Preference summary">Számsor megjelenítése a karakterelrendezés felett</string>
@@ -162,6 +166,8 @@
<string name="pref__keyboard__utility_key_enabled__label" comment="Preference title">Kellékgomb megjelenítése</string>
<string name="pref__keyboard__utility_key_enabled__summary" comment="Preference summary">Egy beállítható kellékgomb megjelenítése a szóköz mellett</string>
<string name="pref__keyboard__utility_key_action__label" comment="Preference title">Kellékgomb művelet</string>
<string name="pref__keyboard__space_bar_language_display_enabled__label" comment="Preference title">Nyelv megjelenítése a szóköz gombon</string>
<string name="pref__keyboard__font_size_multiplier__label" comment="Preference title">Betűméret-szorzó</string>
<string name="pref__keyboard__group_layout__label" comment="Preference group title">Elrendezés</string>
<string name="pref__keyboard__one_handed_mode__label" comment="Preference title">Egykezes mód</string>
<string name="pref__keyboard__one_handed_mode_scale_factor__label" comment="Preference title">Billentyűzet szélessége egykezes módban</string>
@@ -184,6 +190,7 @@
<string name="settings__typing__title" comment="Title of Typing experience screen">Javaslatok és javítások</string>
<string name="pref__suggestion__title" comment="Preference group title">Javaslatok</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">Vágólap tartalom javaslatok</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">Vágólap javaslatok időtúllépése</string>
<string name="pref__correction__title" comment="Preference group title">Javítások</string>
<string name="pref__correction__auto_capitalization__label" comment="Preference title">Automatikusan nagy kezdőbetű</string>
<string name="pref__correction__auto_capitalization__summary" comment="Preference summary">Kezdje nagybetűvel a szavakat a bemeneti környezet alapján</string>
@@ -193,10 +200,11 @@
<string name="pref__correction__double_space_period__summary" comment="Preference summary">Ha kétszer megérinti a szóközt, beszúr egy pontot, amelyet egy szóköz követ</string>
<string name="settings__dictionary__title" comment="Title of the User dictionaries screen">Felhasználói szótárak</string>
<string name="pref__dictionary__enable_system_user_dictionary__label" comment="Preference title">Rendszer felhasználói szótárának engedélyezése</string>
<string name="pref__dictionary__enable_system_user_dictionary__summary" comment="Preference summary">A rendszer felhasználói szótárban tárolt szavak ajánlása</string>
<string name="pref__dictionary__manage_system_user_dictionary__label" comment="Preference title">Rendszer felhasználói szótárának kezelése</string>
<string name="pref__dictionary__manage_system_user_dictionary__summary" comment="Preference summary">Elemek hozzáadása, megtekintése és eltávolítása a rendszer felhasználói szótárból</string>
<string name="pref__dictionary__enable_internal_user_dictionary__label" comment="Preference title">Belső felhasználói szótár engedélyezése</string>
<string name="pref__dictionary__enable_internal_user_dictionary__summary" comment="Preference summary">Tárolt szavak ajánlása a belső felhasználói szótárból</string>
<string name="pref__dictionary__enable_internal_user_dictionary__summary" comment="Preference summary">A belső felhasználói szótárban tárolt szavak ajánlása</string>
<string name="pref__dictionary__manage_floris_user_dictionary__label" comment="Preference title">Belső felhasználói szótár kezelése</string>
<string name="pref__dictionary__manage_floris_user_dictionary__summary" comment="Preference summary">Elemek hozzáadása, megtekintése és eltávolítása a belső felhasználói szótárból</string>
<string name="settings__udm__title_floris" comment="Title of the User Dictionary Manager activity for internal">Belső felhasználói szótár</string>
@@ -497,6 +505,7 @@
<string name="enum__snygg_level__advanced" comment="Enum value label">Haladó</string>
<string name="enum__snygg_level__developer" comment="Enum value label">Fejlesztő</string>
<string name="enum__spelling_language_mode__use_system_languages" comment="Enum value label">Rendszernyelvek használata</string>
<string name="enum__spelling_language_mode__use_keyboard_subtypes" comment="Enum value label">Billentyűzet altípusok használata</string>
<string name="enum__swipe_action__no_action" comment="Enum value label">Nincs művelet</string>
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Váltás az előző billentyűzetmódra</string>
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Váltás a következő billentyűzetmódra</string>

View File

@@ -3,13 +3,13 @@
<string name="app_name">FlorisBoard</string>
<string name="key__phone_pause" comment="Label for the Pause key in the telephone keyboard layout">Jeda</string>
<string name="key__phone_wait" comment="Label for the Wait key in the telephone keyboard layout">Tunggu</string>
<string name="key_popup__threedots_alt" comment="Content description for the three-dots icon in a key popup">Ikon tiga titik. Jika terlihat, menandakan bahwa huruf-huruf tambahan bisa digunakan saat ditekan lama.</string>
<string name="key_popup__threedots_alt" comment="Content description for the three-dots icon in a key popup">Tombol tiga titik. Jika terlihat, menandakan bahwa lebih banyak huruf dapat digunakan jika ditekan lama.</string>
<!-- One-handed strings -->
<string name="one_handed__close_btn_content_description" comment="Content description for the one-handed close button">Tutup mode satu-tangan.</string>
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">Pindahkan keyboard ke kiri.</string>
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">Pindahkan keyboard ke kanan.</string>
<!-- Media strings -->
<string name="settings__media__title">Emoji</string>
<string name="settings__media__title">Emoticon</string>
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emoji</string>
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emotikon</string>
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaomoji</string>
@@ -104,16 +104,23 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
<string name="settings__theme_editor__no_rules_defined">Lembar gaya ini tidak memiliki aturan yang didefinisikan. Tambahkan sebuah aturan untuk mulai menyesuaikan lembar gaya ini.</string>
<string name="settings__theme_editor__rule_already_exists">Aturan lembar gaya ini telah didefinisikan.</string>
<string name="settings__theme_editor__rule_element">Elemen target</string>
<string name="settings__theme_editor__rule_codes">Kode</string>
<string name="settings__theme_editor__rule_codes">Kode target tombol</string>
<string name="settings__theme_editor__rule_groups">Grup</string>
<string name="settings__theme_editor__rule_modes">Mode</string>
<string name="settings__theme_editor__rule_selectors">Pemilih</string>
<string name="settings__theme_editor__add_code">Tambahkan kode</string>
<string name="settings__theme_editor__edit_code">Edit kode</string>
<string name="settings__theme_editor__no_codes_defined">Tidak ada kode yang didefinisikan.</string>
<string name="settings__theme_editor__add_code">Tambahkan kode tombol</string>
<string name="settings__theme_editor__edit_code">Edit kode tombol</string>
<string name="settings__theme_editor__no_codes_defined">Tetapkan aturan ke semua elemen target.</string>
<string name="settings__theme_editor__codes_defined">Tetapkan aturan saja ke element target dengan kode tombol berikut:</string>
<string name="settings__theme_editor__code_already_exists">Kode tombol ini telah didefinisikan.</string>
<string name="settings__theme_editor__code_invalid">Kode tombol ini tidak valid. Pastikan bahwa kode kunci dalam jangkauan dari {c_min} sampai {c_max} untuk karakter atau {i_min} sampai {i_max} untuk tombol internal spesial.</string>
<string name="settings__theme_editor__code_help_text">Tautan berikut ini akan membantu Anda menemukan kode tombol yang sesuai:</string>
<string name="settings__theme_editor__code_help_text">Secara alternatif tautan berikut ini akan membantu Anda menemukan kode tombol yang sesuai:</string>
<string name="settings__theme_editor__code_placeholder">Kode</string>
<string name="settings__theme_editor__code_recording_help_text">Untuk menemukan kode dari sebuah tombol, gunakan tombol di samping kolom input kode. Ketika diaktifkan, itu akan merekam tekanan tombol berikut dan akan mengisi kodenya ke kolom input.</string>
<string name="settings__theme_editor__code_recording_started">Rekaman kode tombol dimulai</string>
<string name="settings__theme_editor__code_recording_stopped">Rekaman kode tombol dihentikan</string>
<string name="settings__theme_editor__code_recording_requires_default_ime_floris">{app_name} harus ditetapkan sebagai keyboard default untuk merekam kode tombol</string>
<string name="settings__theme_editor__code_recording_placeholder">Merekam…</string>
<string name="settings__theme_editor__add_property">Tambahkan properti</string>
<string name="settings__theme_editor__edit_property">Edit properti</string>
<string name="settings__theme_editor__property_already_exists">Sebuah properti dengan nama ini sudah ada dalam aturan saat ini.</string>

View File

@@ -103,16 +103,11 @@
<string name="settings__theme_editor__no_rules_defined">Questo file di stile non ha regole definite. Aggiungi una regola per iniziare a personalizzare questo file di stile.</string>
<string name="settings__theme_editor__rule_already_exists">Questa regola del file di stile è già definita.</string>
<string name="settings__theme_editor__rule_element">Elemento di destinazione</string>
<string name="settings__theme_editor__rule_codes">Codici</string>
<string name="settings__theme_editor__rule_groups">Gruppi</string>
<string name="settings__theme_editor__rule_modes">Modalità</string>
<string name="settings__theme_editor__rule_selectors">Selettori</string>
<string name="settings__theme_editor__add_code">Aggiungi codice</string>
<string name="settings__theme_editor__edit_code">Modifica codice</string>
<string name="settings__theme_editor__no_codes_defined">Nessun codice definito.</string>
<string name="settings__theme_editor__code_already_exists">Questo codice di tasto è già definito.</string>
<string name="settings__theme_editor__code_invalid">Questo codice di tasto non è valido. Assicurarsi che il codice di tasto sia compreso nell\'intervallo da {c_min} a {c_max} per i caratteri o da {i_min} a {i_max} per tasti speciali interni.</string>
<string name="settings__theme_editor__code_help_text">I seguenti link ti aiuteranno a trovare il codice di tasto corrispondente:</string>
<string name="settings__theme_editor__add_property">Aggiungi proprietà</string>
<string name="settings__theme_editor__edit_property">Modifica proprietà</string>
<string name="settings__theme_editor__property_already_exists">Una proprietà con questo nome esiste già all\'interno della regola corrente.</string>

View File

@@ -103,16 +103,11 @@
<string name="settings__theme_editor__no_rules_defined">このスタイルシートにはルールが定義されていません。 このスタイルシートのカスタマイズを開始するルールを追加します。</string>
<string name="settings__theme_editor__rule_already_exists">このスタイルシートルールはすでに定義されています。</string>
<string name="settings__theme_editor__rule_element">ターゲット要素</string>
<string name="settings__theme_editor__rule_codes">コード</string>
<string name="settings__theme_editor__rule_groups">グループ</string>
<string name="settings__theme_editor__rule_modes">モード</string>
<string name="settings__theme_editor__rule_selectors">セレクター</string>
<string name="settings__theme_editor__add_code">コードを追加</string>
<string name="settings__theme_editor__edit_code">コードを編集</string>
<string name="settings__theme_editor__no_codes_defined">コードが定義されていません。</string>
<string name="settings__theme_editor__code_already_exists">このキーコードはすでに定義されています。</string>
<string name="settings__theme_editor__code_invalid">このキーコードは無効です。 キーコードが、文字の場合は {c_min} から {c_max} の範囲内、内部特殊キーの場合は {i_min} から {i_max} の範囲内にあることを確認してください。</string>
<string name="settings__theme_editor__code_help_text">次のリンクは、対応するキーコードを見つけるのに役立ちます:</string>
<string name="settings__theme_editor__add_property">プロパティを追加</string>
<string name="settings__theme_editor__edit_property">プロパティを編集</string>
<string name="settings__theme_editor__property_already_exists">この名前のプロパティは、現在のルール内にすでに存在します。</string>
@@ -178,8 +173,116 @@
<string name="snygg__property_value__explicit_inherit">継承</string>
<string name="snygg__property_value__solid_color">ソリッドカラー</string>
<string name="snygg__property_value__image_ref">画像リファレンス</string>
<string name="snygg__property_value__rectangle_shape">長方形</string>
<string name="snygg__property_value__circle_shape">円形</string>
<string name="snygg__property_value__cut_corner_shape_dp">カットコーナー形状(dp)</string>
<string name="snygg__property_value__cut_corner_shape_percent">カットコーナー形状(%)</string>
<string name="snygg__property_value__rounded_corner_shape_dp">丸みを帯びた角の形(dp)</string>
<string name="snygg__property_value__rounded_corner_shape_percent">丸みを帯びた角の形状(%)</string>
<string name="snygg__property_value__dp_size">サイズ (dp)</string>
<string name="snygg__property_value__sp_size">サイズ (sp)</string>
<string name="snygg__property_value__percentage_size">サイズ (%)</string>
<string name="snygg__property_value__defined_var">変数リファレンス</string>
<string name="settings__input_feedback__title" comment="Title of Input Feedback screen">音とバイブレーション</string>
<string name="pref__input_feedback__group_audio__label" comment="Preference group title">ハウリング</string>
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">ハウリングを有効にする</string>
<string name="pref__input_feedback__audio_enabled__summary" comment="Preference summary">システム設定に応じて、入力イベントのサウンドを再生します</string>
<string name="pref__input_feedback__audio_ignore_system_settings__label" comment="Preference title">システムオーディオ設定を無効</string>
<string name="pref__input_feedback__audio_ignore_system_settings__summary" comment="Preference summary">システムでオーディオが無効になっている場合でも、以下の設定を使用してください。 システムの着信音がミュートされている場合は機能しません</string>
<string name="pref__input_feedback__audio_volume__label" comment="Preference title">入力イベントの音量</string>
<string name="pref__input_feedback__audio_feat_key_press__label" comment="Preference title">キーを押したときのサウンド</string>
<string name="pref__input_feedback__audio_feat_key_long_press__label" comment="Preference title">キーを長く押した時のサウンド</string>
<string name="pref__input_feedback__audio_feat_key_repeated_action__label" comment="Preference title">キーリピートアクションサウンド</string>
<string name="pref__input_feedback__audio_feat_gesture_swipe__label" comment="Preference title">ジェスチャーをスワイプした時のサウンド</string>
<string name="pref__input_feedback__audio_feat_gesture_moving_swipe__label" comment="Preference title">ジェスチャーを移動した時のスワイプサウンド</string>
<string name="pref__input_feedback__group_haptic__label" comment="Preference group title">触覚フィードバック/バイブレーション</string>
<string name="pref__input_feedback__haptic_enabled__label" comment="Preference title">触覚フィードバックを有効</string>
<string name="pref__input_feedback__haptic_enabled__summary" comment="Preference summary">システム設定に応じて、入力イベントを振動させる</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__label" comment="Preference title">システムの触覚設定を無視</string>
<string name="pref__input_feedback__haptic_ignore_system_settings__summary" comment="Preference summary">システムでハプティックが無効になっている場合でも、以下の設定を使用してください</string>
<string name="pref__input_feedback__haptic_use_vibrator__label" comment="Preference title">バイブレーションを直接トリガー</string>
<string name="pref__input_feedback__haptic_use_vibrator__summary" comment="Preference summary">Androidの触覚フィードバック機能セットを使用する代わりに、バイブレーションを直接トリガーします</string>
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">バイブレーション持続時間</string>
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">バイブレーションの強さ</string>
<string name="pref__input_feedback__haptic_vibration_strength__summary_no_amplitude_ctrl" comment="Preference summary">この機能にはハードウェア振幅制御のサポートが必要ですが、これはこのデバイスにはありません</string>
<string name="pref__input_feedback__haptic_vibration_strength__summary_unsupported_android_version" comment="Preference summary">この機能には、Android8.0以降でのみ利用可能な振幅制御のサポートが必要です。</string>
<string name="pref__input_feedback__haptic_feat_key_press__label" comment="Preference title">キーバイブレーション</string>
<string name="pref__input_feedback__haptic_feat_key_long_press__label" comment="Preference title">キーを長く押した時の振動</string>
<string name="pref__input_feedback__haptic_feat_key_repeated_action__label" comment="Preference title">キーリピートアクションバイブレーション</string>
<string name="pref__input_feedback__haptic_feat_gesture_swipe__label" comment="Preference title">ジェスチャーをスワイプした時の振動</string>
<string name="pref__input_feedback__haptic_feat_gesture_moving_swipe__label" comment="Preference title">ジェスチャーを移動した時のスワイプ振動</string>
<string name="pref__input_feedback__any_feat_key_press__summary" comment="Preference summary">例えば キー、ボタン、絵文字タブ</string>
<string name="pref__input_feedback__any_feat_key_long_press__summary" comment="Preference summary">例えば ポップアップメニュー</string>
<string name="pref__input_feedback__any_feat_key_repeated_action__summary" comment="Preference summary">例えばキーを削除</string>
<string name="pref__input_feedback__any_feat_gesture_swipe__summary" comment="Preference summary">実装されていません</string>
<string name="pref__input_feedback__any_feat_gesture_moving_swipe__summary" comment="Preference summary">例えば カーソルコントロールスワイプ</string>
<string name="settings__keyboard__title" comment="Title of Keyboard preferences screen">キーボード</string>
<string name="pref__keyboard__number_row__label" comment="Preference title">行数</string>
<string name="pref__keyboard__number_row__summary" comment="Preference summary">文字レイアウトの上に数字の行を表示する</string>
<string name="pref__keyboard__hinted_number_row_mode__label" comment="Preference title">ヒント付きの数字の行</string>
<string name="pref__keyboard__hinted_symbols_mode__label" comment="Preference title">ヒント記号</string>
<string name="pref__keyboard__utility_key_enabled__label" comment="Preference title">ユーティリティキーを表示</string>
<string name="pref__keyboard__utility_key_enabled__summary" comment="Preference summary">スペースバーの横に設定可能なユーティリティキーを表示します</string>
<string name="pref__keyboard__utility_key_action__label" comment="Preference title">ユーティリティキーアクション</string>
<string name="pref__keyboard__space_bar_language_display_enabled__label" comment="Preference title">スペースバーに言語を表示する</string>
<string name="pref__keyboard__space_bar_language_display_enabled__summary" comment="Preference summary">スペースバーに現在アクティブな言語名を表示します</string>
<string name="pref__keyboard__font_size_multiplier__label" comment="Preference title">フォントサイズの倍率</string>
<string name="pref__keyboard__group_layout__label" comment="Preference group title">レイアウト</string>
<string name="pref__keyboard__one_handed_mode__label" comment="Preference title">片手モード</string>
<string name="pref__keyboard__one_handed_mode_scale_factor__label" comment="Preference title">片手モード時のキーボードの幅</string>
<string name="pref__keyboard__landscape_input_ui_mode__label" comment="Preference value">横向き時のフルスクリーンの入力</string>
<string name="pref__keyboard__height_factor__label" comment="Preference title">キーボードの高さ</string>
<string name="pref__keyboard__key_spacing__label" comment="Preference title">キーの間隔</string>
<string name="pref__keyboard__bottom_offset__label" comment="Preference title">ボトムオフセット</string>
<string name="pref__keyboard__group_keypress__label" comment="Preference group title">キーを押す時の設定</string>
<string name="pref__keyboard__popup_enabled__label" comment="Preference title">ポップアップの可視性</string>
<string name="pref__keyboard__popup_enabled__summary" comment="Preference summary">キーを押すとポップアップが表示されます</string>
<string name="pref__keyboard__merge_hint_popups_enabled__label" comment="Preference title">アクセントにシンボルポップアップを含む</string>
<string name="pref__keyboard__merge_hint_popups_enabled__summary" comment="Preference summary">デフォルトのレイアウトのアクセントにシンボルポップアップを追加します</string>
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">長くキーを押した時の待ち時間</string>
<string name="pref__keyboard__space_bar_switches_to_characters__label" comment="Preference title">スペースバーを文字に切り替える</string>
<string name="pref__keyboard__space_bar_switches_to_characters__summary" comment="Preference summary">記号または数字の場合、自動的に文字に戻ります</string>
<!-- Smartbar strings -->
<string name="settings__smartbar__title" comment="Title of Smartbar screen">スマートバー</string>
<string name="pref__smartbar__enabled__label" comment="Preference title">スマートバーを有効</string>
<string name="pref__smartbar__enabled__summary" comment="Preference summary">キーボードの上に表示されます</string>
<string name="pref__smartbar__flip_toggles__label" comment="Preference title">トグルボタンを切り替える</string>
<string name="pref__smartbar__flip_toggles__summary" comment="Preference summary">アクション行の切り替えを反転します</string>
<string name="pref__smartbar__any_row_type__label" comment="Preference title">行レイアウト</string>
<string name="pref__smartbar__group_primary_actions__label" comment="Preference group title">プライマリーアクション</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__label" comment="Preference title">自動的に拡張/折りたたみ</string>
<string name="pref__smartbar__primary_actions_auto_expand_collapse__summary" comment="Preference summary">現在の状態に基づいてプライマリーアクション行を自動的に展開/折りたたみます</string>
<string name="pref__smartbar__group_secondary_actions__label" comment="Preference group title">セカンダリーアクション</string>
<string name="pref__smartbar__secondary_actions_enabled__label" comment="Preference title">二次アクション行を表示</string>
<!-- Typing strings -->
<string name="settings__typing__title" comment="Title of Typing experience screen">提案&amp; 訂正</string>
<string name="pref__suggestion__title" comment="Preference group title">提案</string>
<string name="pref__suggestion__api30_inline_suggestions_enabled__summary" comment="Preference summary">オートフィルサービスによって提供されるインライン提案を表示する</string>
<string name="pref__suggestion__display_mode__label" comment="Preference title">提案表示モード</string>
<string name="pref__suggestion__clipboard_content_enabled__label" comment="Preference title">クリップボードコンテンツの提案</string>
<string name="pref__suggestion__clipboard_content_timeout__label" comment="Preference title">クリップボードの提案のタイムアウト</string>
<string name="pref__correction__title" comment="Preference group title">訂正</string>
<string name="pref__correction__auto_capitalization__label" comment="Preference title">自動大文字変換</string>
<string name="pref__correction__auto_capitalization__summary" comment="Preference summary">現在の入力コンテキストに基づいて単語を大文字にする</string>
<string name="pref__correction__remember_caps_lock_state__label" comment="Preference title">CapsLock状態を維持</string>
<string name="pref__correction__remember_caps_lock_state__summary" comment="Preference summary">別のテキストフィールドに移動しても、CapsLockはオンのままになります</string>
<string name="pref__correction__double_space_period__label" comment="Preference title">ダブルスペースでピリオドを入力</string>
<string name="pref__correction__double_space_period__summary" comment="Preference summary">スペースバーを2回タップすると、ピリオドの後にスペースが挿入されます</string>
<string name="settings__dictionary__title" comment="Title of the User dictionaries screen">ユーザー辞書</string>
<string name="pref__dictionary__enable_system_user_dictionary__label" comment="Preference title">システムユーザー辞書を有効化</string>
<string name="pref__dictionary__enable_system_user_dictionary__summary" comment="Preference summary">システムユーザー辞書に保存されている単語を提案します</string>
<string name="pref__dictionary__manage_system_user_dictionary__label" comment="Preference title">システムユーザー辞書を管理</string>
<string name="pref__dictionary__manage_system_user_dictionary__summary" comment="Preference summary">システムユーザー辞書のエントリを追加、表示、および削除をします</string>
<string name="pref__dictionary__enable_internal_user_dictionary__label" comment="Preference title">内部ユーザー辞書を有効化</string>
<string name="pref__dictionary__enable_internal_user_dictionary__summary" comment="Preference summary">内部ユーザー辞書に保存されている単語を提案します</string>
<string name="pref__dictionary__manage_floris_user_dictionary__label" comment="Preference title">内部ユーザー辞書を管理</string>
<string name="pref__dictionary__manage_floris_user_dictionary__summary" comment="Preference summary">内部ユーザー辞書のエントリを追加、表示、および削除をします</string>
<string name="settings__udm__title_floris" comment="Title of the User Dictionary Manager activity for internal">内部ユーザー辞書</string>
<string name="settings__udm__title_system" comment="Title of the User Dictionary Manager activity for system">システムユーザー辞書</string>
<string name="settings__udm__no_words_in_dictionary" comment="String to show if no words are present in the dictionary">このユーザー辞書には単語が含まれていません。</string>
<string name="settings__udm__word_summary_freq" comment="Summary label for a word entry. The decimal placeholder inserts the frequency for the word it summarizes.">頻度:{freq}</string>
<string name="settings__udm__word_summary_freq_shortcut" comment="Summary label for a word entry. The first placeholder inserts the frequency for the word it summarizes, the second placeholder the shortcut defined.">頻度:{freq} | ショートカット:{shortcut}</string>
<string name="settings__udm__all_languages" comment="Label of the For all languages entry in the language list">すべての言語</string>
<!-- Spelling UI strings -->
<!-- About UI strings -->
<!-- Setup UI strings -->

View File

@@ -9,9 +9,13 @@
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">키보드를 왼쪽으로 옮깁니다.</string>
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">키보드를 오른쪽으로 옮깁니다.</string>
<!-- Media strings -->
<string name="settings__media__title">이모지</string>
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">이모지</string>
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">이모티콘</string>
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">카오모지</string>
<string name="prefs__media__emoji_recently_used_max_size">최대로 표시할 수 있는 최근 사용한 이모지</string>
<string name="prefs__media__emoji_preferred_skin_tone">선호하는 이모지 피부톤</string>
<string name="prefs__media__emoji_preferred_hair_style">선호하는 이모지 헤어 스타일</string>
<!-- Emoji strings -->
<string name="emoji__category__smileys_emotion" comment="Emoji category name">스마일리 &amp; 이모티콘</string>
<string name="emoji__category__people_body" comment="Emoji category name">사람 &amp;</string>
@@ -22,33 +26,79 @@
<string name="emoji__category__objects" comment="Emoji category name">사물</string>
<string name="emoji__category__symbols" comment="Emoji category name">기호</string>
<string name="emoji__category__flags" comment="Emoji category name">깃발</string>
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">최근에 사용한 이모지가 없습니다. 이모지를 입력하기 시작하면 여기에 자동으로 표시됩니다.</string>
<string name="emoji__recently_used__removal_success_message" comment="Toast message if user has long pressed emoji in recently used collection to remove it">{emoji}가 최근 사용 기록에서 삭제되었습니다</string>
<!-- Smartbar strings -->
<string name="smartbar__quick_action__exit_editing" comment="Content-description for the exit editing layout button in Smartbar">텍스트 수정 패널에서 나갑니다.</string>
<string name="smartbar__quick_action__open_settings" comment="Content-description for the settings quick action in Smartbar">설정을 엽니다.</string>
<string name="smartbar__quick_action__switch_to_editing_context" comment="Content-description for the editing quick action in Smartbar">텍스트 수정 패널로 전환</string>
<string name="smartbar__quick_action__switch_to_media_context" comment="Content-description for the media quick action in Smartbar">미디어 입력 보기로 전환</string>
<!-- Settings UI strings -->
<string name="settings__title" comment="Title of Settings">설정</string>
<string name="settings__preview_keyboard" comment="Hint for try your setup box">설정을 사용해 보세요.</string>
<string name="settings__help" comment="General label for help buttons in Settings">도움말</string>
<string name="settings__default" comment="General string which is used when a preference has the default value set">기본</string>
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">시스템 기본 설정</string>
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBoard가 시스템 설정에서 활성화되어 있지 않아 입력 방법으로 설정할 수 없습니다. 여기를 눌러 문제를 해결할 수 있습니다.</string>
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">FlorisBoard가 기본 키보드로 설정되지 않았습니다. 이 문제를 해결하려면 여기를 클릭하십시오.</string>
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">하위 유형</string>
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">하위 유형 추가</string>
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">하위 유형 편집</string>
<string name="settings__localization__subtype_characters_layout" comment="Label for layout dropdown in subtype dialog">문자 배열</string>
<string name="settings__localization__subtype_currency_set" comment="Label for currency set dropdown in subtype dialog. 'set' is used as a noun here and can be compared to a group of elements (in this case currency symbols).">통화 설정</string>
<string name="settings__localization__subtype_select_locale" comment="Subtype select language title">언어 선택</string>
<string name="settings__localization__subtype_search_locale_placeholder" comment="Subtype search language placeholder">언어 검색하기</string>
<string name="settings__localization__subtype_search_locale_not_found" comment="Subtype search language not found">\"{search_term}\"과 일치하는 언어를 찾지 못했습니다.</string>
<string name="settings__localization__subtype_presets_view_all" comment="View all presets button">모두 보기</string>
<string name="settings__theme__title" comment="Title of the Theme screen">테마</string>
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">테마 모드</string>
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">일출 시간</string>
<string name="pref__theme__sunset_time__label" comment="Label of the sunset time preference">일몰 시간</string>
<string name="pref__theme__day" comment="Label of the day group (day means light theme)">밝은 테마</string>
<string name="pref__theme__night" comment="Label of the night group (night means dark theme)">어두운 테마</string>
<string name="pref__theme__any_theme__label" comment="Label of the theme selector preference">선택한 테마</string>
<string name="pref__theme__source_internal" comment="Label for the theme source field">내부 저장소</string>
<string name="settings__theme_manager__title_day" comment="Title of the theme manager screen for day theme selection">밝은 테마 선택</string>
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">어두운 테마 선택</string>
<string name="settings__theme_manager__title_manage" comment="Title of the theme manager screen for managing installed and custom themes">설치된 테마 관리</string>
<string name="settings__theme_editor__rule_groups">그룹</string>
<string name="snygg__property_name__width">너비</string>
<string name="snygg__property_name__height">높이</string>
<string name="snygg__property_name__border_color">테두리 색</string>
<string name="snygg__property_name__border_style">테두리 스타일</string>
<string name="snygg__property_name__border_width">테두리 두께</string>
<string name="snygg__property_value__dp_size">크기 (dp)</string>
<string name="snygg__property_value__sp_size">크기 (sp)</string>
<string name="snygg__property_value__percentage_size">크기 (%)</string>
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">진동 지속 시간</string>
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">진동 세기</string>
<string name="settings__keyboard__title" comment="Title of Keyboard preferences screen">키보드</string>
<string name="pref__keyboard__one_handed_mode__label" comment="Preference title">한 손 조작 모드</string>
<string name="pref__keyboard__one_handed_mode_scale_factor__label" comment="Preference title">한 손 조작 모드 키보드 너비</string>
<!-- Smartbar strings -->
<string name="pref__smartbar__primary_actions_auto_expand_collapse__label" comment="Preference title">자동 확장/축소</string>
<!-- Typing strings -->
<!-- Spelling UI strings -->
<!-- About UI strings -->
<string name="about__view_source_code" comment="Label of View source code button in About">소스 코드</string>
<string name="about__license__title" comment="Title of Open-source licenses dialog">오픈 소스 라이선스</string>
<string name="about__version__title" comment="Preference title">버전</string>
<!-- Setup UI strings -->
<string name="setup__title" comment="Title of Setup">환영합니다!</string>
<!-- Back up & Restore -->
<!-- Crash Dialog strings -->
<!-- Clipboard strings -->
<string name="clipboard__header_title">클립보드</string>
<string name="clipboard__group_pinned">고정됨</string>
<string name="clipboard__group_other">기타</string>
<string name="clip__delete_item">삭제</string>
<string name="clip__paste_item">붙여넣기</string>
<string name="settings__clipboard__title">클립보드</string>
<!-- Devtools strings -->
<!-- Extension strings -->
<string name="ext__meta__homepage">홈페이지</string>
<string name="ext__meta__id">아이디</string>
<string name="ext__meta__version">버전</string>
<!-- Action strings -->
<!-- Error strings (generic) -->
<!-- General strings -->

View File

@@ -100,13 +100,9 @@
<string name="settings__theme_editor__fine_tune__display_kbd_after_dialogs">Piştî diyalogan klavyeyê nîşande</string>
<string name="settings__theme_editor__add_rule">Rêbazekê tevlî bike</string>
<string name="settings__theme_editor__edit_rule">Rêbazê serrast bike</string>
<string name="settings__theme_editor__rule_codes">Kod</string>
<string name="settings__theme_editor__rule_groups">Kom</string>
<string name="settings__theme_editor__rule_modes">Moda</string>
<string name="settings__theme_editor__rule_selectors">Hilbijêr</string>
<string name="settings__theme_editor__add_code">Koda lê zêde bike</string>
<string name="settings__theme_editor__edit_code">Koda sererastkirinê</string>
<string name="settings__theme_editor__no_codes_defined">Tu kod nehatine pênasekirin.</string>
<string name="settings__theme_editor__code_already_exists">Ev koda mifteyan jixwe hatîye pênasekirin.</string>
<string name="snygg__rule_element__key">Kilîd</string>
<string name="snygg__rule_element__system_nav_bar">Darikê navîgasyonê yê pergalê</string>
@@ -331,6 +327,8 @@
<string name="pref__clipboard__enable_clipboard_history__summary">هەڵگرتنی شتە لەبەرگیراوەکان بۆ هێشتنەوە</string>
<string name="pref__clipboard__clean_up_old__label">Berhemên kevin paqij bikin</string>
<string name="pref__clipboard__clean_up_after__label">Piştî kelûpelên kevin paqij bikin</string>
<string name="pref__clipboard__max_history_size__label">Mezinahiya dîroka Maxê</string>
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__label">Dîroka efektên klîbê yên sereke paqij bike</string>
<!-- Devtools strings -->
<string name="devtools__show_primary_clip__label" comment="Label of Show primary clip in Devtools">پیشاندانی لەبەرگیراوە بنەڕەتیەکان</string>
<string name="devtools__show_primary_clip__summary" comment="Summary of Show primary clip in Devtools">پیشاندانی لەبەرگیراوەی بنەڕەتی سیستەم</string>
@@ -412,6 +410,9 @@
<string name="enum__secondary_row_placement__below_primary__description" comment="Enum value description">پیشاندانی ڕیزی دووەم لەنێوان ڕیزی یەکەم و تەختەکلیل</string>
<string name="enum__secondary_row_placement__overlay_app_ui" comment="Enum value label">لەسەروی تەختەکلیل</string>
<string name="enum__secondary_row_placement__overlay_app_ui__description" comment="Enum value description">پیشادانی ڕیزی دووەم لەسەروی تەختەکلیلەکە، بەبێ گەورەکردنی قەبارەی تەختەکلیل، لەوانەیە ھەندێ بەش بەباشی دەرنەکەون</string>
<string name="enum__snygg_level__basic" comment="Enum value label">Bingehîn</string>
<string name="enum__snygg_level__advanced" comment="Enum value label">Pêşketî</string>
<string name="enum__snygg_level__developer" comment="Enum value label">Pêşdebir</string>
<string name="enum__spelling_language_mode__use_system_languages" comment="Enum value label">Zimanên systemê bi kar bîne</string>
<string name="enum__spelling_language_mode__use_keyboard_subtypes" comment="Enum value label">Cureyên jêr ên klavyeyê bi kar bîne</string>
<string name="enum__swipe_action__no_action" comment="Enum value label">Bê çalakî</string>
@@ -429,7 +430,10 @@
<string name="enum__swipe_action__move_cursor_start_of_page" comment="Enum value label">Nîşanek bibe destpêka rûpelê</string>
<string name="enum__swipe_action__move_cursor_end_of_page" comment="Enum value label">Nîşanek ber bi dawiya rûpelê ve bibe</string>
<string name="enum__swipe_action__shift" comment="Enum value label">Shift</string>
<string name="enum__swipe_action__redo" comment="Enum value label">Redo</string>
<string name="enum__swipe_action__undo" comment="Enum value label">Vegerîne</string>
<string name="enum__swipe_action__select_characters_precisely" comment="Enum value label">Karakteran bi rastî jê bibe</string>
<string name="enum__swipe_action__select_words_precisely" comment="Enum value label">Peyvan bi rastî jê bibire</string>
<string name="enum__swipe_action__show_input_method_picker" comment="Enum value label">Hilbijêrê metoda têkerê nîşan bide</string>
<string name="enum__swipe_action__switch_to_prev_keyboard" comment="Enum value label">Li klavyeya berê biguhere</string>
<string name="enum__swipe_action__switch_to_prev_subtype" comment="Enum value label">Biguhere jêrtîpa berê</string>

View File

@@ -103,16 +103,12 @@
<string name="settings__theme_editor__no_rules_defined">Šī stila lapa nesatur nosacījumus. Lai to pielāgotu, jāpievieno kāds nosacījums.</string>
<string name="settings__theme_editor__rule_already_exists">Šis stila lapas nosacījums jau ir noteikts.</string>
<string name="settings__theme_editor__rule_element">Mērķa elements</string>
<string name="settings__theme_editor__rule_codes">Kodi</string>
<string name="settings__theme_editor__rule_groups">Kopas</string>
<string name="settings__theme_editor__rule_modes">Veidi</string>
<string name="settings__theme_editor__rule_selectors">Atlasītāji</string>
<string name="settings__theme_editor__add_code">Pievienot kodu</string>
<string name="settings__theme_editor__edit_code">Labot kodu</string>
<string name="settings__theme_editor__no_codes_defined">Kodi nav noteikti.</string>
<string name="settings__theme_editor__code_already_exists">Šis taustiņa kods jau ir noteikts.</string>
<string name="settings__theme_editor__code_invalid">Šis taustiņa kods ir nederīgs. Jānodrošina, ka taustiņa kods ir starp {c_min} un {c_max} rakstzīmēm vai starp {i_min} un {i_max} iekšējiem īpašajiem taustiņiem.</string>
<string name="settings__theme_editor__code_help_text">Šīs saites palīdzēs atrast atbilstošo taustiņa kodu:</string>
<string name="settings__theme_editor__code_recording_placeholder">Ieraksta…</string>
<string name="settings__theme_editor__add_property">Pievienot īpašību</string>
<string name="settings__theme_editor__edit_property">Labot īpašību</string>
<string name="settings__theme_editor__property_already_exists">Īpašība ar šādu nosaukumu jau pastāv pašreizējā nosacījumā.</string>

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