Compare commits
48 Commits
v0.4.0
...
feat/flest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f41385ae75 | ||
|
|
5c85be61d9 | ||
|
|
639beb9e64 | ||
|
|
453fb0253a | ||
|
|
13fc7679a2 | ||
|
|
2421d13038 | ||
|
|
7dedfd4f7a | ||
|
|
ef37194900 | ||
|
|
58134b1ceb | ||
|
|
53cfbad404 | ||
|
|
d6f724e518 | ||
|
|
6c4aa36b06 | ||
|
|
edc38b6c2c | ||
|
|
891a2c6bac | ||
|
|
229237153b | ||
|
|
290fbb5239 | ||
|
|
409d4f9348 | ||
|
|
82938cda5b | ||
|
|
f7b0a30271 | ||
|
|
575f359a85 | ||
|
|
22591163b3 | ||
|
|
8104ae60ca | ||
|
|
165b682732 | ||
|
|
eb770fac6c | ||
|
|
39c27426a4 | ||
|
|
228d5055cc | ||
|
|
b400e04560 | ||
|
|
27c1bbf039 | ||
|
|
f61b655f7d | ||
|
|
f82af63e97 | ||
|
|
0fbd950f6e | ||
|
|
e97b5f54ac | ||
|
|
b611360dd5 | ||
|
|
1b9d260020 | ||
|
|
d74fe62bc0 | ||
|
|
fe6f61a282 | ||
|
|
8b4239d9be | ||
|
|
a0c7cf2794 | ||
|
|
7480d14a0f | ||
|
|
7274228a46 | ||
|
|
a38f6a2c76 | ||
|
|
eda6c09538 | ||
|
|
9e42d16cb0 | ||
|
|
11ba51c354 | ||
|
|
51f5196b8a | ||
|
|
56bbe9d13c | ||
|
|
4d1ae52dc0 | ||
|
|
1e1916194b |
@@ -9,7 +9,7 @@ insert_final_newline = true
|
||||
max_line_length = 120
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.har,*.json,*yml}]
|
||||
[{*.har,*.json,*yml,*.sh}]
|
||||
indent_size = 2
|
||||
|
||||
[*.kt]
|
||||
|
||||
2
.github/workflows/crowdin-upload.yml
vendored
2
.github/workflows/crowdin-upload.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Upload
|
||||
uses: crowdin/github-action@1.4.0
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: "crowdin.yml"
|
||||
upload_sources: true
|
||||
|
||||
61
.github/workflows/validate-strings-no-translations.yml
vendored
Normal file
61
.github/workflows/validate-strings-no-translations.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Validate no translated strings.xml included
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Precheck if validation is required
|
||||
id: precheck
|
||||
run: |
|
||||
pr_author="${{ github.event.pull_request.user.login }}"
|
||||
if [[ "$pr_author" == "florisboard-bot" ]]; then
|
||||
echo "PR is by florisboard-bot, skipping validation!"
|
||||
echo "require_validation=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "PR is not by florisboard-bot, requiring validation!"
|
||||
echo "require_validation=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Fetch PR changed files manually
|
||||
id: fetch_changed_files
|
||||
if: steps.precheck.outputs.require_validation == 'true'
|
||||
run: |
|
||||
pr_files="$(curl -sSf https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files?per_page=1000)" || exit 11
|
||||
changed_files="$(jq -r '.[].filename' <<< "$pr_files")" || exit 12
|
||||
illegal_changes_list="$(grep -E '^app/src/main/res/values-.+/strings.xml$' <<< "$changed_files")" || true
|
||||
if [ -n "$illegal_changes_list" ]; then
|
||||
echo -e "Illegal changes detected:\n$illegal_changes_list"
|
||||
else
|
||||
echo "No illegal changes detected"
|
||||
fi
|
||||
echo "illegal_changes_list<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$illegal_changes_list" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create comment if illegal files detected
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
⚠️ Illegal changes detected
|
||||
|
||||
Hey there!
|
||||
|
||||
We detected illegal changes that disobey the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md#translation). This is a kind reminder that pull requests must not contain translated `strings.xml` files, as those are exclusively managed from Crowdin.
|
||||
|
||||
Please remove changes to the following files:
|
||||
```
|
||||
${{ steps.fetch_changed_files.outputs.illegal_changes_list }}
|
||||
```
|
||||
|
||||
- name: Fail workflow if illegal files detected
|
||||
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
|
||||
run: echo -e "Illegal changes detected:\n${{ steps.fetch_changed_files.outputs.illegal_changes_list }}" && exit 1
|
||||
30
README.md
30
README.md
@@ -1,7 +1,7 @@
|
||||
<img align="left" width="80" height="80"
|
||||
src=".github/repo_icon.png" alt="App icon">
|
||||
|
||||
# FlorisBoard [](https://crowdin.florisboard.patrickgold.dev) [](https://matrix.to/#/#florisboard:matrix.org) [](CODE_OF_CONDUCT.md) 
|
||||
# FlorisBoard [](https://crowdin.florisboard.patrickgold.dev) [](https://matrix.to/#/#florisboard:matrix.org) [](CODE_OF_CONDUCT.md) [](https://github.com/florisboard/florisboard/actions/workflows/android.yml)
|
||||
|
||||
**FlorisBoard** is a free and open-source keyboard for Android 7.0+
|
||||
devices. It aims at being modern, user-friendly and customizable while
|
||||
@@ -10,10 +10,10 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<table>
|
||||
<tr>
|
||||
<th align="center" width="50%">
|
||||
<h3>Stable <a href="https://github.com/florisboard/florisboard/releases/latest"><img alt="Latest stable release" src="https://img.shields.io/github/v/release/florisboard/florisboard"></a></h3>
|
||||
<h3>Stable <a href="https://github.com/florisboard/florisboard/releases/latest"><img alt="Latest stable release" src="https://img.shields.io/github/v/release/florisboard/florisboard?sort=semver&display_name=tag&color=28a745"></a></h3>
|
||||
</th>
|
||||
<th align="center" width="50%">
|
||||
<h3>Beta <a href="https://github.com/florisboard/florisboard/releases"><img alt="Latest beta release" src="https://img.shields.io/github/v/release/florisboard/florisboard?include_prereleases"></a></h3>
|
||||
<h3>Preview <a href="https://github.com/florisboard/florisboard/releases"><img alt="Latest preview release" src="https://img.shields.io/github/v/release/florisboard/florisboard?include_prereleases&sort=semver&display_name=tag&color=fd7e14"></a></h3>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -21,7 +21,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<p><i>Major versions only</i><br><br>Updates are more polished, new features are matured and tested through to ensure a stable experience.</p>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<p><i>Alpha/Beta versions</i><br><br>Updates contain new features that may not be fully matured yet and bugs are more likely to occur. Allows you to give early feedback.</p>
|
||||
<p><i>Major + Alpha/Beta/Rc versions</i><br><br>Updates contain new features that may not be fully matured yet and bugs are more likely to occur. Allows you to give early feedback.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -34,6 +34,11 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Obtainium**: [Auto-import stable config][obtainium_stable]
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Manual**: Download and install the APK from the release page.
|
||||
|
||||
</p>
|
||||
@@ -42,7 +47,12 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<p><a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard.beta"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a></p>
|
||||
<p>
|
||||
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [beta testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [preview testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Obtainium**: [Auto-import preview config][obtainium_preview]
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -54,14 +64,13 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Beginning with v0.4.0 FlorisBoard will follow [SemVer](https://semver.org/#summary) versioning scheme.
|
||||
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
## Highlighted features
|
||||
- Integrated clipboard manager / history
|
||||
- Advanced theming support and customization
|
||||
- Integrated extension support (still evolving)
|
||||
- Emoji keyboard
|
||||
- Emoji keyboard / history / suggestions
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Word suggestions/spell checking are not included in the current releases
|
||||
@@ -119,3 +128,10 @@ 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.
|
||||
```
|
||||
|
||||
<!-- BEGIN SECTION: obtainium_links -->
|
||||
<!-- auto-generated link templates, do NOT edit by hand -->
|
||||
<!-- see fastlane/update-readme.sh -->
|
||||
[obtainium_preview]: https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22dev.patrickgold.florisboard.beta%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fflorisboard%2Fflorisboard%22%2C%22author%22%3A%22florisboard%22%2C%22name%22%3A%22FlorisBoard%20Preview%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22preview%5C%22%7D%22%7D%0A
|
||||
[obtainium_stable]: https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22dev.patrickgold.florisboard%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fflorisboard%2Fflorisboard%22%2C%22author%22%3A%22florisboard%22%2C%22name%22%3A%22FlorisBoard%20Stable%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22stable%5C%22%7D%22%7D%0A
|
||||
<!-- END SECTION: obtainium_links -->
|
||||
|
||||
34
ROADMAP.md
34
ROADMAP.md
@@ -2,24 +2,7 @@
|
||||
|
||||
This feature roadmap intents to provide transparency to what is planned to be added to FlorisBoard in the foreseeable future. Note that there are no ETAs for any version milestones down below, experience has shown these won't hold anyways.
|
||||
|
||||
Each major milestone has associated alpha/beta releases, so if you are interested in previewing features quicker, keep an eye out! Each major 0.x release has also patch releases after the initial major release, which will be published on both the stable and beta tracks.
|
||||
|
||||
## 0.4
|
||||
|
||||
**Main focus**: Getting the project back on track, see [this announcement](https://github.com/florisboard/florisboard/discussions/2314) for details. Note that this has also replaced the previous roadmap, however this step is necessary for getting the project back on track again.
|
||||
|
||||
This includes, but is not exclusive to:
|
||||
- Fixing the most reported bugs/issues
|
||||
- Merging in the Material You theme PR -> Adds Material You support (v0.4.0-alpha05)
|
||||
- Merging in other external PRs as best as possible
|
||||
- Reworking the Settings UI warning boxes and hiding any UI for features related to word suggestions until they are ready
|
||||
- Remove existing glide/swipe typing (see 0.5 milestone)
|
||||
- Improvements in clipboard / emoji functionality (v0.4.0-beta01/beta02)
|
||||
- Prepare project to have native code implemented in [Rust](https://www.rust-lang.org/) (v0.4.0-beta02)
|
||||
- - Upgrade Settings UI to Material 3 (v0.4.0-beta03)
|
||||
- Add support for importing extensions via system file handler APIs (relevant for Addons store) (v0.4.0-beta03)
|
||||
|
||||
Note that the previous versioning scheme has been dropped in favor of using a major.minor.patch versioning scheme, so versions like `0.3.16` are a thing of the past :)
|
||||
Each major milestone has associated alpha/beta releases, so if you are interested in previewing features quicker, keep an eye out! Each major 0.x release has also patch releases after the initial major release, which will be published on both the stable and preview tracks.
|
||||
|
||||
## 0.5
|
||||
|
||||
@@ -28,25 +11,25 @@ Note that the previous versioning scheme has been dropped in favor of using a ma
|
||||
- Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
- New text processing logic (maybe moved back / split to 0.6)
|
||||
- Add floating keyboard mode
|
||||
- New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- RFC document with technical details will be released later
|
||||
- New text processing logic (maybe moved back to 0.6)
|
||||
- RFC document with technical details will be released later
|
||||
- Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
- Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- Add support for any remaining new features introduced with Android 13
|
||||
|
||||
## 0.6
|
||||
|
||||
- Complete rework of the Emoji panel
|
||||
- Recently used / Emoji history (already implemented with 0.3.14)
|
||||
- Emoji search
|
||||
- Emoji suggestions when using :emoji_name: syntax (already implemented with v0.4.0-beta02)
|
||||
- Fully scrollable emoji list (soft category borders)
|
||||
- More granular themeing options
|
||||
- Layout customization (e.g. placement of category buttons)
|
||||
- Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.6)
|
||||
- Rework branding images and texts of FlorisBoard for the app stores
|
||||
- Focus on stability and experience improvements of the app and keyboard
|
||||
- Add support for new features introduced with Android 14
|
||||
- Add support for new features introduced with Android 14 / 15
|
||||
- Not finalized, but planned: raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
|
||||
## Backlog / Planned (unassigned)
|
||||
@@ -58,7 +41,6 @@ Note that the previous versioning scheme has been dropped in favor of using a ma
|
||||
- Adaptive themes v2
|
||||
- Voice-to-text with Mozilla's open-source voice service (or any other oss voice provider)
|
||||
- Text translation
|
||||
- Floating keyboard
|
||||
- Stickers/GIFs
|
||||
- Kaomoji panel implementation
|
||||
- FlorisBoard landing web page for presentation
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.plugin.compose)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.mannodermaus.android.junit5)
|
||||
@@ -48,7 +50,6 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xallow-result-return-type",
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-Xjvm-default=all-compatibility",
|
||||
)
|
||||
@@ -99,10 +100,6 @@ android {
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get()
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
@@ -165,14 +162,14 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
composeCompiler {
|
||||
// DO NOT ENABLE STRONG SKIPPING! This project currently relies on
|
||||
// recomposition on parent state change to update the UI correctly.
|
||||
featureFlags.add(ComposeFeatureFlag.StrongSkipping.disabled())
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "282a1b421e498fd0e21c055b6a4315e0",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "clipboard_history",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `text` TEXT, `uri` TEXT, `creationTimestampMs` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `mimeTypes` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `isRemoteDevice` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uri",
|
||||
"columnName": "uri",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "creationTimestampMs",
|
||||
"columnName": "creationTimestampMs",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPinned",
|
||||
"columnName": "isPinned",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeTypes",
|
||||
"columnName": "mimeTypes",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSensitive",
|
||||
"columnName": "isSensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRemoteDevice",
|
||||
"columnName": "isRemoteDevice",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_clipboard_history__id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_clipboard_history__id` ON `${TABLE_NAME}` (`_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '282a1b421e498fd0e21c055b6a4315e0')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -392,6 +392,12 @@
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "tamil",
|
||||
"label": "Tamil",
|
||||
"authors": [ "Clem0908" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "thai_kedmanee",
|
||||
"label": "Thai Kedmanee",
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
{ "code": 11816, "label": "⸨" },
|
||||
{ "code": 10214, "label": "⟦" },
|
||||
{ "code": 10216, "label": "⟨" },
|
||||
{ "code": 10218, "label": "⟩" },
|
||||
{ "code": 10218, "label": "⟪" },
|
||||
{ "code": 123, "label": "{" }
|
||||
]
|
||||
} },
|
||||
@@ -72,7 +72,7 @@
|
||||
"relevant": [
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 11817, "label": "⸩" },
|
||||
{ "code": 10215, "label": "⟦" },
|
||||
{ "code": 10215, "label": "⟧" },
|
||||
{ "code": 10217, "label": "⟩" },
|
||||
{ "code": 10219, "label": "⟫" },
|
||||
{ "code": 125, "label": "}" }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"all": {
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 269, "label": "č" },
|
||||
{ "$": "auto_text_key", "code": 269, "label": "č" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;grinsendes Gesicht;Gesicht|grinsendes Gesicht|lol|lustig
|
||||
😃;grinsendes Gesicht mit großen Augen;Gesicht|grinsendes Gesicht mit großen Augen|lächeln|lol|lustig
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;grinning face;face|grin|grinning face
|
||||
😃;grinning face with big eyes;face|grinning face with big eyes|mouth|open|smile
|
||||
@@ -3788,3 +3791,4 @@
|
||||
🏴;flag: England;flag|flag: England
|
||||
🏴;flag: Scotland;flag|flag: Scotland
|
||||
🏴;flag: Wales;flag|flag: Wales
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;cara sonriendo;cara|cara sonriendo|divertido|feliz|sonrisa
|
||||
😃;cara sonriendo con ojos grandes;cara|cara sonriendo con ojos grandes|divertido|risa|sonriendo
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;visage rieur;sourire|visage rieur
|
||||
😃;visage souriant avec de grands yeux;sourire|visage souriant avec de grands yeux
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;faccina con un gran sorriso;faccina|faccina che sogghigna|faccina con un gran sorriso|risata|sogghignare
|
||||
😃;faccina con un gran sorriso e occhi spalancati;faccina|faccina con un gran sorriso e occhi spalancati|faccina sorridente|risata|sorridere
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;rosto risonho;lol|rindo|risada|rosto|rosto risonho
|
||||
😃;rosto risonho com olhos bem abertos;aberto|boca|rosto|rosto risonho com olhos bem abertos|sorriso
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;;
|
||||
😃;;
|
||||
@@ -3788,3 +3791,4 @@
|
||||
🏴;;
|
||||
🏴;;
|
||||
🏴;;
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -87,6 +87,7 @@ 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.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedPanel
|
||||
import dev.patrickgold.florisboard.ime.sheet.BottomSheetHostUi
|
||||
@@ -370,7 +371,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo { "(no args)" }
|
||||
super.onFinishInput()
|
||||
editorInstance.handleFinishInput()
|
||||
nlpManager.clearInlineSuggestions()
|
||||
NlpInlineAutofill.clearInlineSuggestions()
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
@@ -437,23 +438,26 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? {
|
||||
return if (prefs.smartbar.enabled.get() && prefs.suggestion.api30InlineSuggestionsEnabled.get()) {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Creating inline suggestions request because Smartbar and inline suggestions are enabled."
|
||||
}
|
||||
val stylesBundle = themeManager.createInlineSuggestionUiStyleBundle(this)
|
||||
val spec = InlinePresentationSpec.Builder(InlineSuggestionUiSmallestSize, InlineSuggestionUiBiggestSize)
|
||||
.setStyle(stylesBundle)
|
||||
.build()
|
||||
InlineSuggestionsRequest.Builder(listOf(spec)).let { request ->
|
||||
request.setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED)
|
||||
request.build()
|
||||
}
|
||||
} else {
|
||||
if (!prefs.smartbar.enabled.get() || !prefs.suggestion.api30InlineSuggestionsEnabled.get()) {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Ignoring inline suggestions request because Smartbar and/or inline suggestions are disabled."
|
||||
}
|
||||
null
|
||||
return null
|
||||
}
|
||||
|
||||
flogInfo(LogTopic.IMS_EVENTS) { "Creating inline suggestions request" }
|
||||
val stylesBundle = themeManager.createInlineSuggestionUiStyleBundle(this)
|
||||
val spec = InlinePresentationSpec.Builder(
|
||||
InlineSuggestionUiSmallestSize,
|
||||
InlineSuggestionUiBiggestSize,
|
||||
).run {
|
||||
setStyle(stylesBundle)
|
||||
build()
|
||||
}
|
||||
|
||||
return InlineSuggestionsRequest.Builder(listOf(spec)).run {
|
||||
setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED)
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,8 +467,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Received inline suggestions response with ${inlineSuggestions.size} suggestion(s) provided."
|
||||
}
|
||||
nlpManager.showInlineSuggestions(inlineSuggestions)
|
||||
return true
|
||||
return NlpInlineAutofill.showInlineSuggestions(this, inlineSuggestions)
|
||||
}
|
||||
|
||||
override fun onComputeInsets(outInsets: Insets?) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,8 +31,9 @@ import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHairStyle
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiRecentlyUsedHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
@@ -56,6 +57,8 @@ import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceType
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
@@ -171,6 +174,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "devtools__show_spelling_overlay",
|
||||
default = false,
|
||||
)
|
||||
val showInlineAutofillOverlay = boolean(
|
||||
key = "devtools__show_inline_autofill_overlay",
|
||||
default = false,
|
||||
)
|
||||
val showKeyTouchBoundaries = boolean(
|
||||
key = "devtools__show_touch_boundaries",
|
||||
default = false,
|
||||
@@ -193,6 +200,67 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val emoji = Emoji()
|
||||
inner class Emoji {
|
||||
val preferredSkinTone = enum(
|
||||
key = "emoji__preferred_skin_tone",
|
||||
default = EmojiSkinTone.DEFAULT,
|
||||
)
|
||||
val preferredHairStyle = enum(
|
||||
key = "emoji__preferred_hair_style",
|
||||
default = EmojiHairStyle.DEFAULT,
|
||||
)
|
||||
val historyEnabled = boolean(
|
||||
key = "emoji__history_enabled",
|
||||
default = true,
|
||||
)
|
||||
val historyData = custom(
|
||||
key = "emoji__history_data",
|
||||
default = EmojiHistory.Empty,
|
||||
serializer = EmojiHistory.Serializer,
|
||||
)
|
||||
val historyPinnedUpdateStrategy = enum(
|
||||
key = "emoji__history_pinned_update_strategy",
|
||||
default = EmojiHistory.UpdateStrategy.MANUAL_SORT_PREPEND,
|
||||
)
|
||||
val historyPinnedMaxSize = int(
|
||||
key = "emoji__history_pinned_max_size",
|
||||
default = EmojiHistory.MaxSizeUnlimited,
|
||||
)
|
||||
val historyRecentUpdateStrategy = enum(
|
||||
key = "emoji__history_recent_update_strategy",
|
||||
default = EmojiHistory.UpdateStrategy.AUTO_SORT_PREPEND,
|
||||
)
|
||||
val historyRecentMaxSize = int(
|
||||
key = "emoji__history_recent_max_size",
|
||||
default = 90,
|
||||
)
|
||||
val suggestionEnabled = boolean(
|
||||
key = "emoji__suggestion_enabled",
|
||||
default = true,
|
||||
)
|
||||
val suggestionType = enum(
|
||||
key = "emoji__suggestion_type",
|
||||
default = EmojiSuggestionType.LEADING_COLON,
|
||||
)
|
||||
val suggestionUpdateHistory = boolean(
|
||||
key = "emoji__suggestion_update_history",
|
||||
default = true,
|
||||
)
|
||||
val suggestionCandidateShowName = boolean(
|
||||
key = "emoji__suggestion_candidate_show_name",
|
||||
default = false,
|
||||
)
|
||||
val suggestionQueryMinLength = int(
|
||||
key = "emoji__suggestion_query_min_length",
|
||||
default = 3,
|
||||
)
|
||||
val suggestionCandidateMaxCount = int(
|
||||
key = "emoji__suggestion_candidate_max_count",
|
||||
default = 5,
|
||||
)
|
||||
}
|
||||
|
||||
val gestures = Gestures()
|
||||
inner class Gestures {
|
||||
val swipeUp = enum(
|
||||
@@ -530,27 +598,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val media = Media()
|
||||
inner class Media {
|
||||
val emojiRecentlyUsed = custom(
|
||||
key = "media__emoji_recently_used",
|
||||
default = emptyList(),
|
||||
serializer = EmojiRecentlyUsedHelper.Serializer,
|
||||
)
|
||||
val emojiRecentlyUsedMaxSize = int(
|
||||
key = "media__emoji_recently_used_max_size",
|
||||
default = 90,
|
||||
)
|
||||
val emojiPreferredSkinTone = enum(
|
||||
key = "media__emoji_preferred_skin_tone",
|
||||
default = EmojiSkinTone.DEFAULT,
|
||||
)
|
||||
val emojiPreferredHairStyle = enum(
|
||||
key = "media__emoji_preferred_hair_style",
|
||||
default = EmojiHairStyle.DEFAULT,
|
||||
)
|
||||
}
|
||||
|
||||
val smartbar = Smartbar()
|
||||
inner class Smartbar {
|
||||
val enabled = boolean(
|
||||
@@ -574,6 +621,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "smartbar__shared_actions_expanded",
|
||||
default = false,
|
||||
)
|
||||
@Deprecated("Always enabled due to UX issues")
|
||||
val sharedActionsAutoExpandCollapse = boolean(
|
||||
key = "smartbar__shared_actions_auto_expand_collapse",
|
||||
default = true,
|
||||
@@ -683,8 +731,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
"gestures__space_bar_swipe_right", "gestures__space_bar_long_press", "gestures__delete_key_swipe_left",
|
||||
"gestures__delete_key_long_press", "keyboard__hinted_number_row_mode", "keyboard__hinted_symbols_mode",
|
||||
"keyboard__utility_key_action", "keyboard__one_handed_mode", "keyboard__landscape_input_ui_mode",
|
||||
"localization__display_language_names_in", "media__emoji_preferred_skin_tone",
|
||||
"media__emoji_preferred_hair_style", "smartbar__primary_actions_row_type",
|
||||
"localization__display_language_names_in", "smartbar__primary_actions_row_type",
|
||||
"smartbar__secondary_actions_placement", "smartbar__secondary_actions_row_type", "spelling__language_mode",
|
||||
"suggestion__display_mode", "theme__mode", "theme__editor_display_colors_as",
|
||||
"theme__editor_display_kbd_after_dialogs", "theme__editor_level",
|
||||
@@ -706,6 +753,32 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate media prefs to emoji prefs
|
||||
// Keep migration rule until: 0.6 dev cycle
|
||||
"media__emoji_recently_used" -> {
|
||||
val emojiValues = entry.rawValue.split(";")
|
||||
val recent = emojiValues.map {
|
||||
dev.patrickgold.florisboard.ime.media.emoji.Emoji(it, "", emptyList())
|
||||
}
|
||||
val data = EmojiHistory(emptyList(), recent)
|
||||
entry.transform(key = "emoji__history_data", rawValue = Json.encodeToString(data))
|
||||
}
|
||||
"media__emoji_recently_used_max_size" -> {
|
||||
entry.transform(key = "emoji__history_recent_max_size")
|
||||
}
|
||||
"media__emoji_preferred_skin_tone" -> {
|
||||
entry.transform(
|
||||
key = "emoji__preferred_skin_tone",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
"media__emoji_preferred_hair_style" -> {
|
||||
entry.transform(
|
||||
key = "emoji__preferred_hair_style",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
|
||||
// Default: keep entry
|
||||
else -> entry.keepAsIs()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
@@ -138,6 +140,30 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiHistory.UpdateStrategy::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.AUTO_SORT_PREPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_prepend),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_prepend__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.AUTO_SORT_APPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_append),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_append__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.MANUAL_SORT_PREPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_prepend),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_prepend__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.MANUAL_SORT_APPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_append),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_append__description),
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiSkinTone::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
@@ -184,6 +210,20 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiSuggestionType::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = EmojiSuggestionType.LEADING_COLON,
|
||||
label = stringRes(R.string.enum__emoji_suggestion_type__leading_colon),
|
||||
description = stringRes(R.string.enum__emoji_suggestion_type__leading_colon__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSuggestionType.INLINE_TEXT,
|
||||
label = stringRes(R.string.enum__emoji_suggestion_type__inline_text),
|
||||
description = stringRes(R.string.enum__emoji_suggestion_type__inline_text__description),
|
||||
)
|
||||
}
|
||||
},
|
||||
ExtendedActionsPlacement::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -144,19 +143,19 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
|
||||
if (intent?.action == Intent.ACTION_VIEW && intent.categories?.contains(Intent.CATEGORY_BROWSABLE) == true) {
|
||||
if (intent.action == Intent.ACTION_VIEW && intent.categories?.contains(Intent.CATEGORY_BROWSABLE) == true) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
if (intent?.action == Intent.ACTION_VIEW && intent.data != null) {
|
||||
if (intent.action == Intent.ACTION_VIEW && intent.data != null) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
if (intent?.action == Intent.ACTION_SEND && intent.clipData != null) {
|
||||
if (intent.action == Intent.ACTION_SEND && intent.clipData != null) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
@@ -215,9 +214,5 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
intentToBeHandled = null
|
||||
}
|
||||
|
||||
SideEffect {
|
||||
navController.setOnBackPressedDispatcher(this.onBackPressedDispatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.devtools
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
@@ -40,10 +42,12 @@ import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -57,6 +61,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
|
||||
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
|
||||
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -72,6 +77,9 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (showSpellingOverlay) {
|
||||
DevtoolsSpellingOverlay()
|
||||
}
|
||||
if (showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +125,6 @@ private fun DevtoolsInputStateOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsSpellingOverlay() {
|
||||
val context = LocalContext.current
|
||||
@@ -160,6 +167,25 @@ private fun DevtoolsSpellingOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@Composable
|
||||
private fun DevtoolsInlineAutofillOverlay() {
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
|
||||
DevtoolsOverlayBox(title = "Inline autofill overlay (${inlineSuggestions.size})") {
|
||||
for (inlineSuggestion in inlineSuggestions) {
|
||||
DevtoolsSubGroup(title = "NlpInlineSuggestion") {
|
||||
val info = inlineSuggestion.info
|
||||
DevtoolsText(text = "info.type: ${info.type}")
|
||||
DevtoolsText(text = "info.source: ${info.source}")
|
||||
DevtoolsText(text = "info.isPinned: ${info.isPinned}")
|
||||
val view = inlineSuggestion.view
|
||||
DevtoolsText(text = "view: ${view?.javaClass?.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsOverlayBox(
|
||||
title: String,
|
||||
|
||||
@@ -36,6 +36,7 @@ import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
class DebugOnPurposeCrashException : Exception(
|
||||
"Success! The app crashed purposely to display this beautiful screen we all love :)"
|
||||
@@ -84,6 +85,13 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
summary = stringRes(R.string.devtools__show_spelling_overlay__summary),
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.devtools.showInlineAutofillOverlay,
|
||||
title = stringRes(R.string.devtools__show_inline_autofill_overlay__label),
|
||||
summary = stringRes(R.string.devtools__show_inline_autofill_overlay__summary),
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
visibleIf = { AndroidVersion.ATLEAST_API30_R },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.devtools.showKeyTouchBoundaries,
|
||||
title = stringRes(R.string.devtools__show_key_touch_boundaries__label),
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.devtools
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
@@ -35,19 +37,21 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI and in localization
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
|
||||
@Composable
|
||||
fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = "Debug log"
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
@@ -55,21 +59,36 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
var debugLog by remember { mutableStateOf<List<String>?>(null) }
|
||||
var formattedDebugLog by remember { mutableStateOf<List<String>?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
debugLog = Devtools.generateDebugLog(context, prefs, includeLogcat = true).lines()
|
||||
formattedDebugLog = Devtools.generateDebugLogForGithub(context, prefs, includeLogcat = true).lines()
|
||||
}
|
||||
|
||||
bottomBar {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast("Copied debug log to clipboard")
|
||||
},
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Export (copy to clipboard)",
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
) {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
content {
|
||||
@@ -86,7 +105,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
val log = debugLog
|
||||
if (log == null) {
|
||||
item {
|
||||
Text("Loading...")
|
||||
Text(stringRes(R.string.devtools__debuglog__loading))
|
||||
}
|
||||
} else {
|
||||
items(log) { logLine ->
|
||||
|
||||
@@ -30,9 +30,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Code
|
||||
import androidx.compose.material.icons.outlined.LibraryBooks
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
@@ -322,17 +322,17 @@ private fun EditScreen(
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageMetaData },
|
||||
icon = Icons.Default.Code,
|
||||
title = stringRes(R.string.ext__editor__metadata__title),
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageDependencies },
|
||||
icon = Icons.Outlined.LibraryBooks,
|
||||
icon = Icons.AutoMirrored.Outlined.LibraryBooks,
|
||||
title = stringRes(R.string.ext__editor__dependencies__title),
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageFiles },
|
||||
icon = vectorResource(R.drawable.ic_file_blank),
|
||||
title = stringRes(R.string.ext__editor__files__title),
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -224,7 +224,7 @@ private fun ExtensionMetaRowSimpleText(
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
if (showDividerAbove) {
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
@@ -246,7 +246,7 @@ private fun ExtensionMetaRowScrollableChips(
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
if (showDividerAbove) {
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
|
||||
@@ -25,13 +25,17 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.TriStateCheckbox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.state.ToggleableState
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.FileProvider
|
||||
@@ -86,10 +90,23 @@ object Backup {
|
||||
var clipboardTextItems by mutableStateOf(false)
|
||||
var clipboardImageItems by mutableStateOf(false)
|
||||
var clipboardVideoItems by mutableStateOf(false)
|
||||
var clipboardData by mutableStateOf(false)
|
||||
|
||||
fun validateClipboardCheckbox(): Boolean {
|
||||
return clipboardTextItems && clipboardImageItems && clipboardVideoItems
|
||||
private var _clipboardData: MutableState<ToggleableState> = mutableStateOf(ToggleableState.Off)
|
||||
val clipboardData: State<ToggleableState> = _clipboardData
|
||||
|
||||
fun updateCheckboxState() {
|
||||
val newValue = if (
|
||||
!clipboardVideoItems && !clipboardImageItems && !clipboardTextItems
|
||||
) {
|
||||
ToggleableState.Off
|
||||
} else if (
|
||||
clipboardVideoItems && clipboardImageItems && clipboardTextItems
|
||||
) {
|
||||
ToggleableState.On
|
||||
} else {
|
||||
ToggleableState.Indeterminate
|
||||
}
|
||||
_clipboardData.value = newValue
|
||||
}
|
||||
|
||||
fun provideClipboardItems(): Boolean {
|
||||
@@ -309,28 +326,31 @@ internal fun BackupFilesSelector(
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_theme),
|
||||
)
|
||||
|
||||
CheckboxListItem(
|
||||
TriStateCheckboxListItem(
|
||||
onClick = {
|
||||
if (!filesSelector.clipboardData) {
|
||||
filesSelector.clipboardTextItems = true
|
||||
if (
|
||||
filesSelector.clipboardData.value == ToggleableState.Off ||
|
||||
filesSelector.clipboardData.value == ToggleableState.Indeterminate
|
||||
) {
|
||||
filesSelector.clipboardImageItems = true
|
||||
filesSelector.clipboardVideoItems = true
|
||||
filesSelector.clipboardTextItems = true
|
||||
} else {
|
||||
filesSelector.clipboardTextItems = false
|
||||
filesSelector.clipboardImageItems = false
|
||||
filesSelector.clipboardVideoItems = false
|
||||
filesSelector.clipboardTextItems = false
|
||||
}
|
||||
filesSelector.clipboardData = filesSelector.validateClipboardCheckbox()
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardTextItems && filesSelector.clipboardImageItems && filesSelector.clipboardVideoItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history)
|
||||
state = filesSelector.clipboardData.value,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history),
|
||||
)
|
||||
|
||||
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardTextItems = !filesSelector.clipboardTextItems
|
||||
filesSelector.clipboardData = filesSelector.validateClipboardCheckbox()
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardTextItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_text_items),
|
||||
@@ -339,7 +359,7 @@ internal fun BackupFilesSelector(
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardImageItems = !filesSelector.clipboardImageItems
|
||||
filesSelector.clipboardData = filesSelector.validateClipboardCheckbox()
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardImageItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_image_items),
|
||||
@@ -348,7 +368,7 @@ internal fun BackupFilesSelector(
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardVideoItems = !filesSelector.clipboardVideoItems
|
||||
filesSelector.clipboardData = filesSelector.validateClipboardCheckbox()
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardVideoItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_video_items),
|
||||
@@ -382,6 +402,30 @@ internal fun CheckboxListItem(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TriStateCheckboxListItem(
|
||||
onClick: () -> Unit,
|
||||
state: ToggleableState,
|
||||
text: String,
|
||||
isSecondaryListItem: Boolean = false,
|
||||
) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.rippleClickable(onClick = onClick),
|
||||
icon = {
|
||||
Row {
|
||||
if (isSecondaryListItem) {
|
||||
Spacer(modifier = Modifier.width(40.dp))
|
||||
}
|
||||
TriStateCheckbox(
|
||||
state = state,
|
||||
onClick = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun RadioListItem(
|
||||
onClick: () -> Unit,
|
||||
|
||||
@@ -138,7 +138,10 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -176,15 +179,20 @@ fun RestoreScreen() = FlorisScreen {
|
||||
srcDir.copyRecursively(dstDir, overwrite = true)
|
||||
}
|
||||
}
|
||||
val clipboardManager = context.clipboardManager().value
|
||||
if (shouldReset) {
|
||||
clipboardManager.clearFullHistory()
|
||||
ClipboardFileStorage.resetClipboardFileStorage(context)
|
||||
}
|
||||
|
||||
if (restoreFilesSelector.provideClipboardItems()) {
|
||||
val clipboardFilesDir = workspace.outputDir.subDir("clipboard")
|
||||
val clipboardManager = context.clipboardManager().value
|
||||
|
||||
if (restoreFilesSelector.clipboardTextItems) {
|
||||
val clipboardItems = clipboardFilesDir.subFile(Backup.CLIPBOARD_TEXT_ITEMS_JSON_NAME)
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
clipboardManager.restoreHistory(shouldReset = shouldReset, items = clipboardItemsList.filter { it.type == ItemType.TEXT }, itemType = ItemType.TEXT)
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.TEXT })
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.clipboardImageItems) {
|
||||
@@ -192,14 +200,18 @@ fun RestoreScreen() = FlorisScreen {
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
for (item in clipboardItemsList.filter { it.type == ItemType.IMAGE }) {
|
||||
ClipboardFileStorage.instertFileFromBackup(
|
||||
ClipboardFileStorage.insertFileFromBackupIfNotExisting(
|
||||
context,
|
||||
clipboardFilesDir.subFile(
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${item.uri!!.path!!.split('/').last()}"
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${
|
||||
item.uri!!.path!!.split(
|
||||
'/'
|
||||
).last()
|
||||
}"
|
||||
)
|
||||
)
|
||||
}
|
||||
clipboardManager.restoreHistory(shouldReset = shouldReset, items = clipboardItemsList.filter { it.type == ItemType.IMAGE }, itemType = ItemType.IMAGE)
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.IMAGE })
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.clipboardVideoItems) {
|
||||
@@ -207,14 +219,18 @@ fun RestoreScreen() = FlorisScreen {
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
for (item in clipboardItemsList.filter { it.type == ItemType.VIDEO }) {
|
||||
ClipboardFileStorage.instertFileFromBackup(
|
||||
ClipboardFileStorage.insertFileFromBackupIfNotExisting(
|
||||
context,
|
||||
clipboardFilesDir.subFile(
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${item.uri!!.path!!.split('/').last()}"
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${
|
||||
item.uri!!.path!!.split(
|
||||
'/'
|
||||
).last()
|
||||
}"
|
||||
)
|
||||
)
|
||||
}
|
||||
clipboardManager.restoreHistory(shouldReset = shouldReset, items = clipboardItemsList.filter { it.type == ItemType.VIDEO }, itemType = ItemType.VIDEO)
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.VIDEO })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +254,11 @@ fun RestoreScreen() = FlorisScreen {
|
||||
context.showLongToast(R.string.backup_and_restore__restore__success)
|
||||
navController.navigateUp()
|
||||
} catch (e: Throwable) {
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to e.localizedMessage)
|
||||
e.printStackTrace()
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to e.localizedMessage,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -273,7 +293,10 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
@@ -295,15 +318,15 @@ fun RestoreScreen() = FlorisScreen {
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = stringRes(R.string.backup_and_restore__restore__metadata),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Default.Code,
|
||||
title = workspace.metadata.packageName,
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Outlined.Info,
|
||||
title = "${workspace.metadata.versionName} (${workspace.metadata.versionCode})",
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Default.Schedule,
|
||||
title = remember(workspace.metadata.timestamp) {
|
||||
val formatter = DateFormat.getDateTimeInstance()
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
@@ -193,7 +193,7 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
icon = if (currentLocale != null) {
|
||||
Icons.Default.Close
|
||||
} else {
|
||||
Icons.Default.ArrowBack
|
||||
Icons.AutoMirrored.Filled.ArrowBack
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { navController.navigate(
|
||||
Routes.Ext.Import(ExtensionImportScreenType.EXT_LANGUAGEPACK, null)
|
||||
) },
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.localization
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
@@ -24,8 +26,13 @@ import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -33,8 +40,8 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.keyboard.LayoutType
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -46,7 +53,20 @@ import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
internal val SubtypeSaver = Saver<MutableState<Subtype?>, String>(
|
||||
save = {
|
||||
Json.encodeToString<Subtype?>(it.value)
|
||||
},
|
||||
restore = {
|
||||
mutableStateOf(Json.decodeFromString(it))
|
||||
},
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LocalizationScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__title)
|
||||
@@ -57,7 +77,7 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val subtypeManager by context.subtypeManager()
|
||||
val cacheManager by context.cacheManager()
|
||||
var chosenSubtypeToDelete: Subtype? by rememberSaveable(saver = SubtypeSaver) { mutableStateOf(null) }
|
||||
|
||||
floatingActionButton {
|
||||
ExtendedFloatingActionButton(
|
||||
@@ -84,7 +104,6 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
entries = enumDisplayEntriesOf(DisplayLanguageNamesIn::class),
|
||||
)
|
||||
Preference(
|
||||
// icon = R.drawable.ic_edit,
|
||||
title = stringRes(R.string.settings__localization__language_pack_title),
|
||||
summary = stringRes(R.string.settings__localization__language_pack_summary),
|
||||
onClick = {
|
||||
@@ -118,17 +137,50 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
DisplayLanguageNamesIn.NATIVE_LOCALE -> subtype.primaryLocale.displayName(subtype.primaryLocale)
|
||||
},
|
||||
summary = summary,
|
||||
onClick = {
|
||||
navController.navigate(
|
||||
Routes.Settings.SubtypeEdit(subtype.id)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.combinedClickable(
|
||||
onClick = {
|
||||
navController.navigate(
|
||||
Routes.Settings.SubtypeEdit(subtype.id)
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
chosenSubtypeToDelete = subtype
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//PreferenceGroup(title = stringRes(R.string.settings__localization__group_layouts__label)) {
|
||||
//}
|
||||
DeleteSubtypeConfirmationDialog(
|
||||
subtypeToDelete = chosenSubtypeToDelete,
|
||||
onDismiss = {
|
||||
chosenSubtypeToDelete = null
|
||||
},
|
||||
onConfirm = {
|
||||
chosenSubtypeToDelete?.let { subtypeManager.removeSubtype(subtypeToRemove = it) }
|
||||
chosenSubtypeToDelete = null
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeleteSubtypeConfirmationDialog(
|
||||
subtypeToDelete: Subtype?,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
subtypeToDelete?.let {
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.settings__localization__subtype_delete_confirmation_title),
|
||||
confirmLabel = stringRes(R.string.action__yes),
|
||||
dismissLabel = stringRes(R.string.action__no),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
) {
|
||||
Text(stringRes(R.string.settings__localization__subtype_delete_confirmation_warning))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,43 +16,126 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.media
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.EmojiSymbols
|
||||
import androidx.compose.material.icons.outlined.Schedule
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.pluralsRes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
fun MediaScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__media__title)
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = false
|
||||
iconSpaceReserved = true
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
prefs.media.emojiPreferredSkinTone,
|
||||
prefs.emoji.preferredSkinTone,
|
||||
title = stringRes(R.string.prefs__media__emoji_preferred_skin_tone),
|
||||
entries = enumDisplayEntriesOf(EmojiSkinTone::class),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.media.emojiRecentlyUsedMaxSize,
|
||||
title = stringRes(R.string.prefs__media__emoji_recently_used_max_size),
|
||||
valueLabel = { maxSize ->
|
||||
if (maxSize == 0) {
|
||||
stringRes(R.string.general__unlimited)
|
||||
} else {
|
||||
pluralsRes(R.plurals.unit__items__written, maxSize, "v" to maxSize)
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 120,
|
||||
stepIncrement = 1,
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.prefs__media__emoji_history__title)) {
|
||||
SwitchPreference(
|
||||
prefs.emoji.historyEnabled,
|
||||
icon = Icons.Outlined.Schedule,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_enabled),
|
||||
summary = stringRes(R.string.prefs__media__emoji_history_enabled__summary),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.historyPinnedUpdateStrategy,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_pinned_update_strategy),
|
||||
entries = enumDisplayEntriesOf(EmojiHistory.UpdateStrategy::class),
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.historyRecentUpdateStrategy,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_recent_update_strategy),
|
||||
entries = enumDisplayEntriesOf(EmojiHistory.UpdateStrategy::class),
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.emoji.historyPinnedMaxSize,
|
||||
secondaryPref = prefs.emoji.historyRecentMaxSize,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_max_size),
|
||||
primaryLabel = stringRes(R.string.emoji__history__pinned),
|
||||
secondaryLabel = stringRes(R.string.emoji__history__recent),
|
||||
valueLabel = { maxSize ->
|
||||
if (maxSize == EmojiHistory.MaxSizeUnlimited) {
|
||||
stringRes(R.string.general__unlimited)
|
||||
} else {
|
||||
pluralsRes(R.plurals.unit__items__written, maxSize, "v" to maxSize)
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 120,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.prefs__media__emoji_suggestion__title)) {
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionEnabled,
|
||||
icon = Icons.Outlined.EmojiSymbols,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_enabled),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_enabled__summary),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.suggestionType,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_type),
|
||||
entries = enumDisplayEntriesOf(EmojiSuggestionType::class),
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionUpdateHistory,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_update_history),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_update_history__summary),
|
||||
enabledIf = {
|
||||
prefs.emoji.suggestionEnabled.isTrue() && prefs.emoji.historyEnabled.isTrue()
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionCandidateShowName,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_candidate_show_name),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_candidate_show_name__summary),
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.emoji.suggestionQueryMinLength,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_query_min_length),
|
||||
valueLabel = { length ->
|
||||
pluralsRes(R.plurals.unit__characters__written, length, "v" to length)
|
||||
},
|
||||
min = 1,
|
||||
max = 5,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.emoji.suggestionCandidateMaxCount,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_candidate_max_count),
|
||||
valueLabel = { count ->
|
||||
pluralsRes(R.plurals.unit__candidates__written, count, "v" to count)
|
||||
},
|
||||
min = 1,
|
||||
max = 10,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package dev.patrickgold.florisboard.app.settings.smartbar
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
@@ -64,11 +65,16 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
prefs.smartbar.layout isEqualTo SmartbarLayout.SUGGESTIONS_ACTIONS_EXTENDED
|
||||
},
|
||||
)
|
||||
// TODO: schedule to remove this preference in the future, but keep it for now so users
|
||||
// know why the setting is not available anymore. Also force enable it for UI display.
|
||||
SideEffect {
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
}
|
||||
SwitchPreference(
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse,
|
||||
title = stringRes(R.string.pref__smartbar__shared_actions_auto_expand_collapse__label),
|
||||
summary = stringRes(R.string.pref__smartbar__shared_actions_auto_expand_collapse__summary),
|
||||
enabledIf = { prefs.smartbar.enabled isEqualTo true },
|
||||
summary = "[Since v0.4.1] Always enabled due to UX issues",
|
||||
enabledIf = { false },
|
||||
visibleIf = { prefs.smartbar.layout isEqualTo SmartbarLayout.SUGGESTIONS_ACTIONS_SHARED },
|
||||
)
|
||||
ListPreference(
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.automirrored.filled.HelpOutline
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -455,7 +455,7 @@ private fun PropertyValueEditor(
|
||||
FlorisIconButton(
|
||||
onClick = { showSyntaxHelp = !showSyntaxHelp },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.HelpOutline,
|
||||
icon = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -40,8 +40,8 @@ import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Pageview
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -225,7 +225,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
},
|
||||
selected = pressedSelector,
|
||||
color = if (pressedSelector) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { focusSelector = !focusSelector },
|
||||
@@ -235,7 +234,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
},
|
||||
selected = focusSelector,
|
||||
color = if (focusSelector) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { disabledSelector = !disabledSelector },
|
||||
@@ -244,7 +242,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
},
|
||||
selected = disabledSelector,
|
||||
color = if (disabledSelector) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -291,7 +288,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.enum__input_shift_state__unshifted)
|
||||
},
|
||||
selected = shiftStateUnshifted,
|
||||
color = if (shiftStateUnshifted) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedManual = !shiftStateShiftedManual },
|
||||
@@ -302,7 +298,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_manual)
|
||||
},
|
||||
selected = shiftStateShiftedManual,
|
||||
color = if (shiftStateShiftedManual) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedAutomatic = !shiftStateShiftedAutomatic },
|
||||
@@ -313,7 +308,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_automatic)
|
||||
},
|
||||
selected = shiftStateShiftedAutomatic,
|
||||
color = if (shiftStateShiftedAutomatic) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateCapsLock = !shiftStateCapsLock },
|
||||
@@ -324,7 +318,6 @@ internal fun EditRuleDialog(
|
||||
else -> stringRes(R.string.enum__input_shift_state__caps_lock)
|
||||
},
|
||||
selected = shiftStateCapsLock,
|
||||
color = if (shiftStateCapsLock) MaterialTheme.colorScheme.secondary else Color.Unspecified,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -479,7 +472,7 @@ private fun EditCodeValueDialog(
|
||||
FlorisIconButton(
|
||||
onClick = { showKeyCodesHelp = !showKeyCodesHelp },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.HelpOutline,
|
||||
icon = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -48,15 +48,16 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.ToggleOff
|
||||
import androidx.compose.material.icons.filled.ToggleOn
|
||||
import androidx.compose.material.icons.filled.Videocam
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -87,6 +88,8 @@ 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.media.KeyboardLikeButton
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -193,6 +196,13 @@ fun ClipboardInputLayout(
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
enabled = !deviceLocked && historyEnabled && !isPopupSurfaceActive(),
|
||||
)
|
||||
KeyboardLikeButton(
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.DELETE,
|
||||
element = FlorisImeUi.ClipboardHeader,
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Outlined.Backspace, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +222,7 @@ fun ClipboardInputLayout(
|
||||
clip = true,
|
||||
clickAndSemanticsModifier = Modifier.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
indication = ripple(),
|
||||
enabled = popupItem == null,
|
||||
onLongClick = {
|
||||
popupItem = item
|
||||
@@ -307,7 +317,7 @@ fun ClipboardInputLayout(
|
||||
.fillMaxWidth()
|
||||
.run { if (contentScrollInsteadOfClip) this.florisVerticalScroll() else this }
|
||||
.padding(ItemPadding),
|
||||
text = text,
|
||||
text = item.displayText(),
|
||||
style = TextStyle(textDirection = TextDirection.ContentOrLtr),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize(),
|
||||
@@ -577,7 +587,7 @@ fun ClipboardInputLayout(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
.height(FlorisImeSizing.imeUiHeight()),
|
||||
) {
|
||||
HeaderRow()
|
||||
if (deviceLocked) {
|
||||
|
||||
@@ -28,11 +28,6 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDatabase
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import org.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -45,6 +40,11 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import org.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import java.io.Closeable
|
||||
|
||||
@@ -110,7 +110,9 @@ class ClipboardManager(
|
||||
val primaryClipFlow = _primaryClipFlow.asStateFlow()
|
||||
inline var primaryClip
|
||||
get() = primaryClipFlow.value
|
||||
private set(v) { _primaryClipFlow.value = v }
|
||||
private set(v) {
|
||||
_primaryClipFlow.value = v
|
||||
}
|
||||
|
||||
init {
|
||||
systemClipboardManager.addPrimaryClipChangedListener(this)
|
||||
@@ -278,6 +280,9 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all unpinned items from the clipboard history
|
||||
*/
|
||||
fun clearHistory() {
|
||||
ioScope.launch {
|
||||
for (item in history().all) {
|
||||
@@ -287,6 +292,9 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the full clipboard history
|
||||
*/
|
||||
fun clearFullHistory() {
|
||||
ioScope.launch {
|
||||
for (item in history().all) {
|
||||
@@ -300,26 +308,15 @@ class ClipboardManager(
|
||||
/**
|
||||
* Restore the clipboard history from a [List]
|
||||
*
|
||||
* @param shouldReset if the history should be reset
|
||||
* @param items the [ClipboardItem] list with the new items
|
||||
*/
|
||||
fun restoreHistory(items: List<ClipboardItem>, shouldReset: Boolean, itemType: ItemType) {
|
||||
fun restoreHistory(items: List<ClipboardItem>) {
|
||||
ioScope.launch {
|
||||
if (shouldReset) {
|
||||
for (item in history().all) {
|
||||
item.close(appContext)
|
||||
}
|
||||
clipHistoryDao?.deleteAllFromType(itemType)
|
||||
for (item in items) {
|
||||
val currentHistory = this@ClipboardManager.history().all
|
||||
for (item in items) {
|
||||
if (!currentHistory.map { it.copy(id = 0) }.contains(item.copy(id = 0))) {
|
||||
this@ClipboardManager.insertClip(item.copy(id = 0))
|
||||
}
|
||||
} else {
|
||||
val currentHistory = this@ClipboardManager.history().all
|
||||
for (item in items) {
|
||||
if (!currentHistory.map { it.copy(id = 0) }.contains(item.copy(id = 0))) {
|
||||
this@ClipboardManager.insertClip(item.copy(id = 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,7 +341,7 @@ class ClipboardManager(
|
||||
|
||||
fun unpinClip(item: ClipboardItem) {
|
||||
ioScope.launch {
|
||||
clipHistoryDao?.update(item.copy(isPinned = false))
|
||||
clipHistoryDao?.update(item.copy(isPinned = false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
} else {
|
||||
val uri: Uri? =
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
} else {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard.provider
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription.EXTRA_IS_REMOTE_DEVICE
|
||||
import android.content.ClipDescription.EXTRA_IS_SENSITIVE
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
@@ -24,6 +26,8 @@ import android.net.Uri
|
||||
import android.provider.BaseColumns
|
||||
import android.provider.MediaStore.Images.Media
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.ColumnInfo
|
||||
@@ -39,9 +43,14 @@ import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.Update
|
||||
import dev.patrickgold.florisboard.R
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.UriSerializer
|
||||
import org.florisboard.lib.android.query
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
|
||||
private const val CLIPBOARD_HISTORY_TABLE = "clipboard_history"
|
||||
@@ -67,7 +76,7 @@ enum class ItemType(val value: Int) {
|
||||
*/
|
||||
@Serializable
|
||||
@Entity(tableName = CLIPBOARD_HISTORY_TABLE)
|
||||
data class ClipboardItem(
|
||||
data class ClipboardItem @OptIn(ExperimentalSerializationApi::class) constructor(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = BaseColumns._ID, index = true)
|
||||
var id: Long = 0,
|
||||
@@ -78,6 +87,10 @@ data class ClipboardItem(
|
||||
val creationTimestampMs: Long,
|
||||
val isPinned: Boolean,
|
||||
val mimeTypes: Array<String>,
|
||||
@EncodeDefault
|
||||
val isSensitive: Boolean = false,
|
||||
@EncodeDefault
|
||||
val isRemoteDevice: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -113,6 +126,18 @@ data class ClipboardItem(
|
||||
else -> ItemType.TEXT
|
||||
}
|
||||
|
||||
val isSensitive = if (AndroidVersion.ATLEAST_API33_T) {
|
||||
data.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val isRemoteDevice = if (AndroidVersion.ATLEAST_API34_U) {
|
||||
data.description?.extras?.getBoolean(EXTRA_IS_REMOTE_DEVICE) ?: false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val uri = if (type == ItemType.IMAGE || type == ItemType.VIDEO) {
|
||||
if (dataItem.uri.authority == ClipboardMediaProvider.AUTHORITY || !cloneUri) {
|
||||
dataItem.uri
|
||||
@@ -151,7 +176,21 @@ data class ClipboardItem(
|
||||
}
|
||||
}
|
||||
|
||||
return ClipboardItem(0, type, text, uri, System.currentTimeMillis(), false, mimeTypes)
|
||||
return ClipboardItem(0, type, text, uri, System.currentTimeMillis(), false, mimeTypes, isSensitive, isRemoteDevice)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
inline fun displayText(): String {
|
||||
val context = LocalContext.current
|
||||
return displayText(context)
|
||||
}
|
||||
|
||||
fun displayText(context: Context): String {
|
||||
return if (isSensitive) {
|
||||
context.stringRes(R.string.clipboard__sensitive_clip_content)
|
||||
} else {
|
||||
stringRepresentation()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +332,7 @@ interface ClipboardHistoryDao {
|
||||
fun deleteAllUnpinned()
|
||||
}
|
||||
|
||||
@Database(entities = [ClipboardItem::class], version = 2)
|
||||
@Database(entities = [ClipboardItem::class], version = 3)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class ClipboardHistoryDatabase : RoomDatabase() {
|
||||
abstract fun clipboardItemDao(): ClipboardHistoryDao
|
||||
|
||||
@@ -61,7 +61,28 @@ object ClipboardFileStorage {
|
||||
return context.clipboardFilesDir.subFile(id.toString())
|
||||
}
|
||||
|
||||
fun instertFileFromBackup(context: Context, file: FsFile) {
|
||||
file.copyTo(context.clipboardFilesDir.subFile(file.name), overwrite = false)
|
||||
|
||||
/**
|
||||
* Insert file from backup if not existing
|
||||
*
|
||||
* @param context the application context
|
||||
* @param file the file to be inserted
|
||||
*/
|
||||
fun insertFileFromBackupIfNotExisting(context: Context, file: FsFile) {
|
||||
if (!context.clipboardFilesDir.subFile(file.name).isFile) {
|
||||
file.copyTo(context.clipboardFilesDir.subFile(file.name), overwrite = false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files from the clipboard subdirectory
|
||||
*
|
||||
* @param context the application context
|
||||
*/
|
||||
fun resetClipboardFileStorage(context: Context) {
|
||||
context.clipboardFilesDir.listFiles()?.forEach {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,15 @@ package dev.patrickgold.florisboard.ime.keyboard
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowRightAlt
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowRightAlt
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardReturn
|
||||
import androidx.compose.material.icons.automirrored.filled.Redo
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||
import androidx.compose.material.icons.automirrored.outlined.Assignment
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.ContentCut
|
||||
@@ -27,24 +35,16 @@ import androidx.compose.material.icons.filled.DeleteSweep
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material.icons.filled.KeyboardCapslock
|
||||
import androidx.compose.material.icons.filled.KeyboardReturn
|
||||
import androidx.compose.material.icons.filled.KeyboardVoice
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.MoreHoriz
|
||||
import androidx.compose.material.icons.filled.Redo
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.SelectAll
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material.icons.filled.SentimentSatisfiedAlt
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.SpaceBar
|
||||
import androidx.compose.material.icons.filled.Undo
|
||||
import androidx.compose.material.icons.outlined.Assignment
|
||||
import androidx.compose.material.icons.outlined.Backspace
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
@@ -179,10 +179,10 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
val evaluator = this
|
||||
return when (data.code) {
|
||||
KeyCode.ARROW_LEFT -> {
|
||||
Icons.Default.KeyboardArrowLeft
|
||||
Icons.AutoMirrored.Filled.KeyboardArrowLeft
|
||||
}
|
||||
KeyCode.ARROW_RIGHT -> {
|
||||
Icons.Default.KeyboardArrowRight
|
||||
Icons.AutoMirrored.Filled.KeyboardArrowRight
|
||||
}
|
||||
KeyCode.ARROW_UP -> {
|
||||
Icons.Default.KeyboardArrowUp
|
||||
@@ -213,23 +213,23 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
Icons.Default.KeyboardVoice
|
||||
}
|
||||
KeyCode.DELETE -> {
|
||||
Icons.Outlined.Backspace
|
||||
Icons.AutoMirrored.Outlined.Backspace
|
||||
}
|
||||
KeyCode.ENTER -> {
|
||||
val imeOptions = evaluator.editorInfo.imeOptions
|
||||
val inputAttributes = evaluator.editorInfo.inputAttributes
|
||||
if (imeOptions.flagNoEnterAction || inputAttributes.flagTextMultiLine) {
|
||||
Icons.Default.KeyboardReturn
|
||||
Icons.AutoMirrored.Filled.KeyboardReturn
|
||||
} else {
|
||||
when (imeOptions.action) {
|
||||
ImeOptions.Action.DONE -> Icons.Default.Done
|
||||
ImeOptions.Action.GO -> Icons.Default.ArrowRightAlt
|
||||
ImeOptions.Action.NEXT -> Icons.Default.ArrowRightAlt
|
||||
ImeOptions.Action.NONE -> Icons.Default.KeyboardReturn
|
||||
ImeOptions.Action.PREVIOUS -> Icons.Default.ArrowRightAlt
|
||||
ImeOptions.Action.GO -> Icons.AutoMirrored.Filled.ArrowRightAlt
|
||||
ImeOptions.Action.NEXT -> Icons.AutoMirrored.Filled.ArrowRightAlt
|
||||
ImeOptions.Action.NONE -> Icons.AutoMirrored.Filled.KeyboardReturn
|
||||
ImeOptions.Action.PREVIOUS -> Icons.AutoMirrored.Filled.ArrowRightAlt
|
||||
ImeOptions.Action.SEARCH -> Icons.Default.Search
|
||||
ImeOptions.Action.SEND -> Icons.Default.Send
|
||||
ImeOptions.Action.UNSPECIFIED -> Icons.Default.KeyboardReturn
|
||||
ImeOptions.Action.SEND -> Icons.AutoMirrored.Filled.Send
|
||||
ImeOptions.Action.UNSPECIFIED -> Icons.AutoMirrored.Filled.KeyboardReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
Icons.Default.SentimentSatisfiedAlt
|
||||
}
|
||||
KeyCode.IME_UI_MODE_CLIPBOARD -> {
|
||||
Icons.Outlined.Assignment
|
||||
Icons.AutoMirrored.Outlined.Assignment
|
||||
}
|
||||
KeyCode.LANGUAGE_SWITCH -> {
|
||||
Icons.Default.Language
|
||||
@@ -263,10 +263,10 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
}
|
||||
}
|
||||
KeyCode.UNDO -> {
|
||||
Icons.Default.Undo
|
||||
Icons.AutoMirrored.Filled.Undo
|
||||
}
|
||||
KeyCode.REDO -> {
|
||||
Icons.Default.Redo
|
||||
Icons.AutoMirrored.Filled.Redo
|
||||
}
|
||||
KeyCode.TOGGLE_ACTIONS_OVERFLOW -> {
|
||||
Icons.Default.MoreHoriz
|
||||
|
||||
@@ -142,9 +142,15 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
prefs.keyboard.utilityKeyEnabled.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyAction.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
activeState.collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
subtypeManager.subtypesFlow.collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) {
|
||||
reevaluateInputShiftState()
|
||||
updateActiveEvaluators()
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Backspace
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -101,7 +101,7 @@ fun MediaInputLayout(
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.DELETE,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Backspace, contentDescription = null)
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Backspace, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,12 +113,13 @@ internal fun KeyboardLikeButton(
|
||||
modifier: Modifier = Modifier,
|
||||
inputEventDispatcher: InputEventDispatcher,
|
||||
keyData: KeyData,
|
||||
element: String = FlorisImeUi.EmojiKey,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
var isPressed by remember { mutableStateOf(false) }
|
||||
val keyStyle = FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.EmojiKey,
|
||||
element = element,
|
||||
code = keyData.code,
|
||||
isPressed = isPressed,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,11 @@ 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.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.util.stream.IntStream
|
||||
import kotlin.streams.toList
|
||||
|
||||
@@ -42,7 +47,7 @@ enum class EmojiHairStyle(val id: Int) {
|
||||
BALD(0x1F9B3);
|
||||
}
|
||||
|
||||
data class Emoji(val value: String, val name: String, val keywords: List<String>) : KeyData {
|
||||
class Emoji(val value: String, val name: String, val keywords: List<String>) : KeyData {
|
||||
override val type = KeyType.CHARACTER
|
||||
override val code = KeyCode.UNSPECIFIED
|
||||
override val label = value
|
||||
@@ -73,4 +78,24 @@ data class Emoji(val value: String, val name: String, val keywords: List<String>
|
||||
override fun toString(): String {
|
||||
return "Emoji { value=$value, name=$name, keywords=$keywords }"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return value.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is Emoji && value == other.value
|
||||
}
|
||||
|
||||
object ValueOnlySerializer : KSerializer<Emoji> {
|
||||
override val descriptor = PrimitiveSerialDescriptor("EmojiValueOnly", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Emoji) {
|
||||
encoder.encodeString(value.value)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Emoji {
|
||||
return Emoji(decoder.decodeString(), "", emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,11 @@ data class EmojiData(
|
||||
// Assume it is a data line
|
||||
val data = line.split(";")
|
||||
if (data.size == 3) {
|
||||
val base = emojiEditorList?.first()
|
||||
val emoji = Emoji(
|
||||
value = data[0].trim(),
|
||||
name = data[1].trim(),
|
||||
keywords = data[2].split("|").map { it.trim() }
|
||||
name = base?.name ?: data[1].trim(),
|
||||
keywords = data[2].split("|").map { it.trim() },
|
||||
)
|
||||
if (emojiEditorList != null) {
|
||||
emojiEditorList!!.add(emoji)
|
||||
@@ -113,6 +114,14 @@ data class EmojiData(
|
||||
|
||||
for (category in byCategory.keys) {
|
||||
for (emojiSet in byCategory[category]!!) {
|
||||
if (emojiSet.emojis.size == 1) {
|
||||
// No variations provided, we fallback to using the base for all skin tones
|
||||
val base = emojiSet.emojis.first()
|
||||
for (skinTone in EmojiSkinTone.entries) {
|
||||
bySkinTone[skinTone]!!.add(base)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for (emoji in emojiSet.emojis) {
|
||||
bySkinTone[emoji.skinTone]!!.add(emoji)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
data class EmojiHistory(
|
||||
val pinned: List<@Serializable(with = Emoji.ValueOnlySerializer::class) Emoji>,
|
||||
val recent: List<@Serializable(with = Emoji.ValueOnlySerializer::class) Emoji>,
|
||||
) {
|
||||
fun edit(): Editor {
|
||||
return Editor(pinned.toMutableList(), recent.toMutableList())
|
||||
}
|
||||
|
||||
data class Editor(
|
||||
val pinned: MutableList<Emoji>,
|
||||
val recent: MutableList<Emoji>,
|
||||
) {
|
||||
fun build(): EmojiHistory {
|
||||
return EmojiHistory(pinned.toList(), recent.toList())
|
||||
}
|
||||
}
|
||||
|
||||
enum class UpdateStrategy(val isAutomatic: Boolean, val isPrepend: Boolean) {
|
||||
AUTO_SORT_PREPEND(isAutomatic = true, isPrepend = true),
|
||||
AUTO_SORT_APPEND(isAutomatic = true, isPrepend = false),
|
||||
MANUAL_SORT_PREPEND(isAutomatic = false, isPrepend = true),
|
||||
MANUAL_SORT_APPEND(isAutomatic = false, isPrepend = false);
|
||||
}
|
||||
|
||||
object Serializer : PreferenceSerializer<EmojiHistory> {
|
||||
override fun serialize(value: EmojiHistory): String {
|
||||
return Json.encodeToString(value)
|
||||
}
|
||||
|
||||
override fun deserialize(value: String): EmojiHistory {
|
||||
try {
|
||||
return Json.decodeFromString(value)
|
||||
} catch (e: Exception) {
|
||||
flogError { "Failed to deserialize EmojiHistory: $e" }
|
||||
return Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Empty = EmojiHistory(emptyList(), emptyList())
|
||||
|
||||
@Suppress("ConstPropertyName")
|
||||
const val MaxSizeUnlimited: Int = 0
|
||||
}
|
||||
}
|
||||
|
||||
object EmojiHistoryHelper {
|
||||
private var emojiGuard = Mutex(locked = false)
|
||||
|
||||
suspend fun markEmojiUsed(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
val dataMut = prefs.emoji.historyData.get().edit()
|
||||
val pinnedUS = prefs.emoji.historyPinnedUpdateStrategy.get()
|
||||
val recentUS = prefs.emoji.historyRecentUpdateStrategy.get()
|
||||
val pinnedMaxSize = prefs.emoji.historyPinnedMaxSize.get().let { maxSize ->
|
||||
if (maxSize == EmojiHistory.MaxSizeUnlimited) Int.MAX_VALUE else maxSize
|
||||
}
|
||||
val recentMaxSize = prefs.emoji.historyRecentMaxSize.get().let { maxSize ->
|
||||
if (maxSize == EmojiHistory.MaxSizeUnlimited) Int.MAX_VALUE else maxSize
|
||||
}
|
||||
|
||||
val pinnedIndex = dataMut.pinned.indexOf(emoji)
|
||||
if (pinnedIndex != -1) {
|
||||
if (pinnedUS.isAutomatic) {
|
||||
dataMut.pinned.removeAt(pinnedIndex)
|
||||
dataMut.pinned.addWithStrategy(pinnedUS, emoji)
|
||||
} else {
|
||||
// manual sort, keep item in place
|
||||
}
|
||||
} else {
|
||||
val recentIndex = dataMut.recent.indexOf(emoji)
|
||||
if (recentIndex != -1) {
|
||||
if (recentUS.isAutomatic) {
|
||||
dataMut.recent.removeAt(recentIndex)
|
||||
dataMut.recent.addWithStrategy(recentUS, emoji)
|
||||
} else {
|
||||
// manual sort, keep item in place
|
||||
}
|
||||
} else {
|
||||
dataMut.recent.addWithStrategy(recentUS, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
prefs.emoji.historyData.set(
|
||||
EmojiHistory(
|
||||
pinned = dataMut.pinned.takeWithStrategy(pinnedUS, pinnedMaxSize),
|
||||
recent = dataMut.recent.takeWithStrategy(recentUS, recentMaxSize),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun pinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
val dataMut = prefs.emoji.historyData.get().edit()
|
||||
val pinnedUS = prefs.emoji.historyPinnedUpdateStrategy.get()
|
||||
|
||||
val recentIndex = dataMut.recent.indexOf(emoji)
|
||||
if (recentIndex != -1) {
|
||||
dataMut.recent.removeAt(recentIndex)
|
||||
dataMut.pinned.addWithStrategy(pinnedUS, emoji)
|
||||
}
|
||||
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun unpinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
val dataMut = prefs.emoji.historyData.get().edit()
|
||||
val recentUS = prefs.emoji.historyRecentUpdateStrategy.get()
|
||||
|
||||
val pinnedIndex = dataMut.pinned.indexOf(emoji)
|
||||
if (pinnedIndex != -1) {
|
||||
dataMut.pinned.removeAt(pinnedIndex)
|
||||
dataMut.recent.addWithStrategy(recentUS, emoji)
|
||||
}
|
||||
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun moveEmoji(prefs: AppPrefs, emoji: Emoji, offset: Int) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get() || offset == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val dataMut = prefs.emoji.historyData.get().edit()
|
||||
|
||||
val pinnedIndex = dataMut.pinned.indexOf(emoji)
|
||||
if (pinnedIndex != -1) {
|
||||
dataMut.pinned.move(pinnedIndex, offset)
|
||||
} else {
|
||||
val recentIndex = dataMut.recent.indexOf(emoji)
|
||||
if (recentIndex != -1) {
|
||||
dataMut.recent.move(recentIndex, offset)
|
||||
}
|
||||
}
|
||||
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
val dataMut = prefs.emoji.historyData.get().edit()
|
||||
|
||||
val pinnedIndex = dataMut.pinned.indexOf(emoji)
|
||||
if (pinnedIndex != -1) {
|
||||
dataMut.pinned.removeAt(pinnedIndex)
|
||||
} else {
|
||||
val recentIndex = dataMut.recent.indexOf(emoji)
|
||||
if (recentIndex != -1) {
|
||||
dataMut.recent.removeAt(recentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
private fun MutableList<Emoji>.addWithStrategy(strategy: EmojiHistory.UpdateStrategy, emoji: Emoji) {
|
||||
if (strategy.isPrepend) {
|
||||
add(0, emoji)
|
||||
} else {
|
||||
add(emoji)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<Emoji>.takeWithStrategy(
|
||||
strategy: EmojiHistory.UpdateStrategy,
|
||||
n: Int,
|
||||
): List<Emoji> {
|
||||
return if (strategy.isPrepend) {
|
||||
take(n)
|
||||
} else {
|
||||
takeLast(n)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<Emoji>.move(itemIndex: Int, offset: Int) {
|
||||
val newIndex = (itemIndex + offset).coerceIn(0..<size)
|
||||
val item = removeAt(itemIndex)
|
||||
if (newIndex == size) {
|
||||
add(item)
|
||||
} else {
|
||||
add(newIndex, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,14 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.shape.GenericShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
@@ -61,6 +67,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
@@ -85,12 +92,11 @@ import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.header
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
@@ -117,6 +123,12 @@ private val VariantsTriangleShapeRtl = GenericShape { size, _ ->
|
||||
lineTo(x = 0f, y = size.height)
|
||||
}
|
||||
|
||||
data class EmojiMappingForView(
|
||||
val pinned: List<EmojiSet>,
|
||||
val recent: List<EmojiSet>,
|
||||
val simple: List<EmojiSet>,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun EmojiPaletteView(
|
||||
fullEmojiMappings: EmojiData,
|
||||
@@ -150,16 +162,61 @@ fun EmojiPaletteView(
|
||||
|
||||
val deviceLocked = androidKeyguardManager.let { it.isDeviceLocked || it.isKeyguardLocked }
|
||||
|
||||
var activeCategory by remember { mutableStateOf(EmojiCategory.RECENTLY_USED) }
|
||||
val lazyListState = rememberLazyGridState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val preferredSkinTone by prefs.media.emojiPreferredSkinTone.observeAsState()
|
||||
val preferredSkinTone by prefs.emoji.preferredSkinTone.observeAsState()
|
||||
val emojiHistoryEnabled by prefs.emoji.historyEnabled.observeAsState()
|
||||
val fontSizeMultiplier = prefs.keyboard.fontSizeMultiplier()
|
||||
val emojiKeyStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiKey)
|
||||
val emojiKeyFontSize = emojiKeyStyle.fontSize.spSize(default = EmojiDefaultFontSize) safeTimes fontSizeMultiplier
|
||||
val contentColor = emojiKeyStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
|
||||
var activeCategory by remember(emojiHistoryEnabled) {
|
||||
if (emojiHistoryEnabled) {
|
||||
mutableStateOf(EmojiCategory.RECENTLY_USED)
|
||||
} else {
|
||||
mutableStateOf(EmojiCategory.SMILEYS_EMOTION)
|
||||
}
|
||||
}
|
||||
var recentlyUsedVersion by remember { mutableIntStateOf(0) }
|
||||
val lazyListState = rememberLazyGridState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@Composable
|
||||
fun GridHeader(text: String) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EmojiKeyWrapper(
|
||||
emojiSet: EmojiSet,
|
||||
isPinned: Boolean = false,
|
||||
isRecent: Boolean = false,
|
||||
) {
|
||||
EmojiKey(
|
||||
emojiSet = emojiSet,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
preferredSkinTone = preferredSkinTone,
|
||||
isPinned = isPinned,
|
||||
isRecent = isRecent,
|
||||
contentColor = contentColor,
|
||||
fontSize = emojiKeyFontSize,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiInput = { emoji ->
|
||||
keyboardManager.inputEventDispatcher.sendDownUp(emoji)
|
||||
scope.launch {
|
||||
EmojiHistoryHelper.markEmojiUsed(prefs, emoji)
|
||||
}
|
||||
},
|
||||
onHistoryAction = {
|
||||
recentlyUsedVersion++
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
EmojiCategoriesTabRow(
|
||||
activeCategory = activeCategory,
|
||||
@@ -167,6 +224,7 @@ fun EmojiPaletteView(
|
||||
scope.launch { lazyListState.scrollToItem(0) }
|
||||
activeCategory = category
|
||||
},
|
||||
emojiHistoryEnabled = emojiHistoryEnabled,
|
||||
)
|
||||
|
||||
Box(
|
||||
@@ -174,16 +232,25 @@ fun EmojiPaletteView(
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
var recentlyUsedVersion by remember { mutableIntStateOf(0) }
|
||||
val emojiMapping = if (activeCategory == EmojiCategory.RECENTLY_USED) {
|
||||
// Purposely using remember here to prevent recomposition, as this would cause rapid
|
||||
// emoji changes for the user when in recently used category.
|
||||
remember(recentlyUsedVersion) {
|
||||
prefs.media.emojiRecentlyUsed.get().map { EmojiSet(listOf(it)) }
|
||||
val data = prefs.emoji.historyData.get()
|
||||
EmojiMappingForView(
|
||||
pinned = data.pinned.map { EmojiSet(listOf(it)) },
|
||||
recent = data.recent.map { EmojiSet(listOf(it)) },
|
||||
simple = emptyList(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
emojiMappings[activeCategory]!!
|
||||
EmojiMappingForView(
|
||||
pinned = emptyList(),
|
||||
recent = emptyList(),
|
||||
simple = emojiMappings[activeCategory]!!,
|
||||
)
|
||||
}
|
||||
val isEmojiHistoryEmpty = emojiMapping.pinned.isEmpty() && emojiMapping.recent.isEmpty()
|
||||
if (activeCategory == EmojiCategory.RECENTLY_USED && deviceLocked) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -191,29 +258,28 @@ fun EmojiPaletteView(
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__recently_used__phone_locked_message),
|
||||
text = stringRes(R.string.emoji__history__phone_locked_message),
|
||||
color = contentColor,
|
||||
)
|
||||
}
|
||||
} else if (activeCategory == EmojiCategory.RECENTLY_USED && emojiMapping.isEmpty()) {
|
||||
} else if (activeCategory == EmojiCategory.RECENTLY_USED && isEmojiHistoryEmpty) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__recently_used__empty_message),
|
||||
text = stringRes(R.string.emoji__history__empty_message),
|
||||
color = contentColor,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = stringRes(R.string.emoji__recently_used__removal_tip),
|
||||
text = stringRes(R.string.emoji__history__usage_tip),
|
||||
color = contentColor,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
else key(emojiMapping) {
|
||||
} else key(emojiMapping) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
@@ -222,35 +288,26 @@ fun EmojiPaletteView(
|
||||
columns = GridCells.Adaptive(minSize = EmojiBaseWidth),
|
||||
state = lazyListState,
|
||||
) {
|
||||
items(emojiMapping) { emojiSet ->
|
||||
EmojiKey(
|
||||
emojiSet = emojiSet,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
preferredSkinTone = preferredSkinTone,
|
||||
contentColor = contentColor,
|
||||
fontSize = emojiKeyFontSize,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiInput = { emoji ->
|
||||
keyboardManager.inputEventDispatcher.sendDownUp(emoji)
|
||||
scope.launch {
|
||||
EmojiRecentlyUsedHelper.addEmoji(prefs, emoji)
|
||||
}
|
||||
},
|
||||
onLongPress = { emoji ->
|
||||
if (activeCategory == EmojiCategory.RECENTLY_USED) {
|
||||
scope.launch {
|
||||
EmojiRecentlyUsedHelper.removeEmoji(prefs, emoji)
|
||||
recentlyUsedVersion++
|
||||
withContext(Dispatchers.Main) {
|
||||
context.showShortToast(
|
||||
R.string.emoji__recently_used__removal_success_message,
|
||||
"emoji" to emoji.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
if (emojiMapping.pinned.isNotEmpty()) {
|
||||
header("header_pinned") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__pinned))
|
||||
}
|
||||
items(emojiMapping.pinned) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isPinned = true)
|
||||
}
|
||||
}
|
||||
if (emojiMapping.recent.isNotEmpty()) {
|
||||
header("header_recent") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__recent))
|
||||
}
|
||||
items(emojiMapping.recent) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isRecent = true)
|
||||
}
|
||||
}
|
||||
if (emojiMapping.simple.isNotEmpty()) {
|
||||
items(emojiMapping.simple) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,6 +320,7 @@ fun EmojiPaletteView(
|
||||
private fun EmojiCategoriesTabRow(
|
||||
activeCategory: EmojiCategory,
|
||||
onCategoryChange: (EmojiCategory) -> Unit,
|
||||
emojiHistoryEnabled: Boolean,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
@@ -271,7 +329,11 @@ private fun EmojiCategoriesTabRow(
|
||||
val unselectedContentColor = tabStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
val selectedContentColor = tabStyleFocused.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
|
||||
val selectedTabIndex = EmojiCategoryValues.indexOf(activeCategory)
|
||||
val selectedTabIndex = if (emojiHistoryEnabled) {
|
||||
EmojiCategoryValues.indexOf(activeCategory)
|
||||
} else {
|
||||
EmojiCategoryValues.indexOf(activeCategory) - 1
|
||||
}
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -288,6 +350,9 @@ private fun EmojiCategoriesTabRow(
|
||||
},
|
||||
) {
|
||||
for (category in EmojiCategoryValues) {
|
||||
if (category == EmojiCategory.RECENTLY_USED && !emojiHistoryEnabled) {
|
||||
continue
|
||||
}
|
||||
Tab(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
|
||||
@@ -311,11 +376,13 @@ private fun EmojiKey(
|
||||
emojiSet: EmojiSet,
|
||||
emojiCompatInstance: EmojiCompat?,
|
||||
preferredSkinTone: EmojiSkinTone,
|
||||
isPinned: Boolean,
|
||||
isRecent: Boolean,
|
||||
contentColor: Color,
|
||||
fontSize: TextUnit,
|
||||
fontSizeMultiplier: Float,
|
||||
onEmojiInput: (Emoji) -> Unit,
|
||||
onLongPress: (Emoji) -> Unit,
|
||||
onHistoryAction: () -> Unit,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val base = emojiSet.base(withSkinTone = preferredSkinTone)
|
||||
@@ -335,8 +402,7 @@ private fun EmojiKey(
|
||||
},
|
||||
onLongPress = {
|
||||
inputFeedbackController.keyLongPress(TextKeyData.UNSPECIFIED)
|
||||
onLongPress(base)
|
||||
if (variations.isNotEmpty()) {
|
||||
if (variations.isNotEmpty() || isPinned || isRecent) {
|
||||
showVariantsBox = true
|
||||
}
|
||||
},
|
||||
@@ -350,7 +416,7 @@ private fun EmojiKey(
|
||||
color = contentColor,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
if (variations.isNotEmpty()) {
|
||||
if (variations.isNotEmpty() || isPinned || isRecent) {
|
||||
val shape = when (LocalLayoutDirection.current) {
|
||||
LayoutDirection.Ltr -> VariantsTriangleShapeLtr
|
||||
LayoutDirection.Rtl -> VariantsTriangleShapeRtl
|
||||
@@ -364,19 +430,34 @@ private fun EmojiKey(
|
||||
)
|
||||
}
|
||||
|
||||
EmojiVariationsPopup(
|
||||
variations = variations,
|
||||
visible = showVariantsBox,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiTap = { emoji ->
|
||||
onEmojiInput(emoji)
|
||||
showVariantsBox = false
|
||||
},
|
||||
onDismiss = {
|
||||
showVariantsBox = false
|
||||
},
|
||||
)
|
||||
if (isPinned || isRecent) {
|
||||
EmojiHistoryPopup(
|
||||
emoji = base,
|
||||
visible = showVariantsBox,
|
||||
isCurrentlyPinned = isPinned,
|
||||
onHistoryAction = {
|
||||
onHistoryAction()
|
||||
showVariantsBox = false
|
||||
},
|
||||
onDismiss = {
|
||||
showVariantsBox = false
|
||||
},
|
||||
)
|
||||
} else {
|
||||
EmojiVariationsPopup(
|
||||
variations = variations,
|
||||
visible = showVariantsBox,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiTap = { emoji ->
|
||||
onEmojiInput(emoji)
|
||||
showVariantsBox = false
|
||||
},
|
||||
onDismiss = {
|
||||
showVariantsBox = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +515,113 @@ private fun EmojiVariationsPopup(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun EmojiHistoryPopup(
|
||||
emoji: Emoji,
|
||||
visible: Boolean,
|
||||
isCurrentlyPinned: Boolean,
|
||||
onHistoryAction: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val scope = rememberCoroutineScope()
|
||||
val popupStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiKeyPopup)
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
val pinnedUS by prefs.emoji.historyPinnedUpdateStrategy.observeAsState()
|
||||
val recentUS by prefs.emoji.historyRecentUpdateStrategy.observeAsState()
|
||||
val showMoveLeft = isCurrentlyPinned && !pinnedUS.isAutomatic || !recentUS.isAutomatic
|
||||
val showMoveRight = isCurrentlyPinned && !pinnedUS.isAutomatic || !recentUS.isAutomatic
|
||||
|
||||
@Composable
|
||||
fun Action(icon: ImageVector, action: suspend () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
scope.launch {
|
||||
action()
|
||||
onHistoryAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
.width(EmojiBaseWidth)
|
||||
.height(emojiKeyHeight)
|
||||
.padding(all = 4.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = popupStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val numActions = 1
|
||||
if (visible) {
|
||||
Popup(
|
||||
alignment = Alignment.TopCenter,
|
||||
offset = with(LocalDensity.current) {
|
||||
val y = -emojiKeyHeight * ceil(numActions / 6f)
|
||||
IntOffset(x = 0, y = y.toPx().toInt())
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.widthIn(max = EmojiBaseWidth * 6)
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor()),
|
||||
) {
|
||||
if (isCurrentlyPinned) {
|
||||
Action(
|
||||
icon = Icons.Outlined.PushPin,
|
||||
action = {
|
||||
EmojiHistoryHelper.unpinEmoji(prefs, emoji)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Action(
|
||||
icon = Icons.Outlined.PushPin,
|
||||
action = {
|
||||
EmojiHistoryHelper.pinEmoji(prefs, emoji)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (showMoveLeft) {
|
||||
Action(
|
||||
icon = Icons.AutoMirrored.Default.KeyboardArrowLeft,
|
||||
action = {
|
||||
EmojiHistoryHelper.moveEmoji(prefs, emoji, -1)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (showMoveRight) {
|
||||
Action(
|
||||
icon = Icons.AutoMirrored.Default.KeyboardArrowRight,
|
||||
action = {
|
||||
EmojiHistoryHelper.moveEmoji(prefs, emoji, 1)
|
||||
},
|
||||
)
|
||||
}
|
||||
Action(
|
||||
icon = Icons.Outlined.Delete,
|
||||
action = {
|
||||
EmojiHistoryHelper.removeEmoji(prefs, emoji)
|
||||
context.showShortToast(
|
||||
R.string.emoji__history__removal_success_message,
|
||||
"emoji" to emoji.value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EmojiText(
|
||||
text: String,
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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.ime.media.emoji
|
||||
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
object EmojiRecentlyUsedHelper {
|
||||
private const val DELIMITER = ";"
|
||||
|
||||
private var emojiGuard = Mutex(locked = false)
|
||||
|
||||
suspend fun addEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
val maxSize = prefs.media.emojiRecentlyUsedMaxSize.get()
|
||||
val list = prefs.media.emojiRecentlyUsed.get().toMutableList()
|
||||
list.add(0, emoji)
|
||||
if (maxSize > 0) {
|
||||
while (list.size > maxSize) {
|
||||
list.removeLast()
|
||||
}
|
||||
}
|
||||
prefs.media.emojiRecentlyUsed.set(list.distinctBy { it.value })
|
||||
}
|
||||
|
||||
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
val list = prefs.media.emojiRecentlyUsed.get().toMutableList()
|
||||
list.remove(emoji)
|
||||
prefs.media.emojiRecentlyUsed.set(list.distinctBy { it.value })
|
||||
}
|
||||
|
||||
object Serializer : PreferenceSerializer<List<Emoji>> {
|
||||
override fun serialize(value: List<Emoji>): String {
|
||||
return value.joinToString(DELIMITER) { it.value }
|
||||
}
|
||||
|
||||
override fun deserialize(value: String): List<Emoji> {
|
||||
return value.split(DELIMITER).mapNotNull { rawValue ->
|
||||
rawValue.trim().let { if (it.isBlank()) null else Emoji(it.trim(), "", emptyList()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -13,10 +29,6 @@ import dev.patrickgold.florisboard.ime.nlp.SuggestionProvider
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import io.github.reactivecircus.cache4k.Cache
|
||||
|
||||
const val EMOJI_SUGGESTION_INDICATOR = ':'
|
||||
const val EMOJI_SUGGESTION_MAX_COUNT = 5
|
||||
private const val EMOJI_SUGGESTION_QUERY_MIN_LENGTH = 3
|
||||
|
||||
/**
|
||||
* Provides emoji suggestions within a text input context.
|
||||
*
|
||||
@@ -30,7 +42,7 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
|
||||
override val providerId = "org.florisboard.nlp.providers.emoji"
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val lettersRegex = "^:[A-Za-z]*$".toRegex()
|
||||
private val lettersRegex = "^[A-Za-z]*$".toRegex()
|
||||
|
||||
private val cachedEmojiMappings = Cache.Builder().build<FlorisLocale, EmojiDataBySkinTone>()
|
||||
|
||||
@@ -52,7 +64,8 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
|
||||
allowPossiblyOffensive: Boolean,
|
||||
isPrivateSession: Boolean
|
||||
): List<SuggestionCandidate> {
|
||||
val preferredSkinTone = prefs.media.emojiPreferredSkinTone.get()
|
||||
val preferredSkinTone = prefs.emoji.preferredSkinTone.get()
|
||||
val showName = prefs.emoji.suggestionCandidateShowName.get()
|
||||
val query = validateInputQuery(content.composingText) ?: return emptyList()
|
||||
val emojis = cachedEmojiMappings.get(subtype.primaryLocale)?.get(preferredSkinTone) ?: emptyList()
|
||||
val candidates = withContext(Dispatchers.Default) {
|
||||
@@ -62,14 +75,24 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
|
||||
emoji.keywords.any { it.contains(query, ignoreCase = true) }
|
||||
}
|
||||
.limit(maxCandidateCount.toLong())
|
||||
.map { EmojiSuggestionCandidate(it) }
|
||||
.map { emoji ->
|
||||
EmojiSuggestionCandidate(
|
||||
emoji = emoji,
|
||||
showName = showName,
|
||||
sourceProvider = this@EmojiSuggestionProvider,
|
||||
)
|
||||
}
|
||||
.collect(Collectors.toList())
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
override suspend fun notifySuggestionAccepted(subtype: Subtype, candidate: SuggestionCandidate) {
|
||||
// No-op
|
||||
val updateHistory = prefs.emoji.suggestionUpdateHistory.get()
|
||||
if (!updateHistory || candidate !is EmojiSuggestionCandidate) {
|
||||
return
|
||||
}
|
||||
EmojiHistoryHelper.markEmojiUsed(prefs, candidate.emoji)
|
||||
}
|
||||
|
||||
override suspend fun notifySuggestionReverted(subtype: Subtype, candidate: SuggestionCandidate) {
|
||||
@@ -90,15 +113,18 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
|
||||
* Validates the user input query for emoji suggestions.
|
||||
*/
|
||||
private fun validateInputQuery(composingText: CharSequence): String? {
|
||||
if (!composingText.startsWith(EMOJI_SUGGESTION_INDICATOR)) {
|
||||
val prefix = prefs.emoji.suggestionType.get().prefix
|
||||
val queryMinLength = prefs.emoji.suggestionQueryMinLength.get() + prefix.length
|
||||
if (prefix.isNotEmpty() && !composingText.startsWith(prefix)) {
|
||||
return null
|
||||
}
|
||||
if (composingText.length <= EMOJI_SUGGESTION_QUERY_MIN_LENGTH) {
|
||||
if (composingText.length < queryMinLength) {
|
||||
return null
|
||||
}
|
||||
if (!lettersRegex.matches(composingText)) {
|
||||
val emojiPartialName = composingText.substring(prefix.length)
|
||||
if (!lettersRegex.matches(emojiPartialName)) {
|
||||
return null
|
||||
}
|
||||
return composingText.substring(1)
|
||||
return emojiPartialName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
enum class EmojiSuggestionType(val prefix: String) {
|
||||
LEADING_COLON(":"),
|
||||
INLINE_TEXT(""),
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.nlp
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Size
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InlineSuggestion
|
||||
import android.view.inputmethod.InlineSuggestionInfo
|
||||
import android.widget.inline.InlineContentView
|
||||
import androidx.annotation.RequiresApi
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogInfo
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
data class NlpInlineAutofillSuggestion(
|
||||
val info: InlineSuggestionInfo,
|
||||
val view: InlineContentView?,
|
||||
)
|
||||
|
||||
object NlpInlineAutofill {
|
||||
private val currentSequenceId = AtomicInteger(0)
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val setterGuard = Mutex()
|
||||
private val _suggestions = MutableStateFlow<List<NlpInlineAutofillSuggestion>>(emptyList())
|
||||
val suggestions = _suggestions
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun showInlineSuggestions(context: Context, rawSuggestions: List<InlineSuggestion>): Boolean {
|
||||
val sequenceId = generateSequenceId()
|
||||
|
||||
if (rawSuggestions.isEmpty()) {
|
||||
clearInlineSuggestions(sequenceId)
|
||||
return false
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val size = Size(ViewGroup.LayoutParams.WRAP_CONTENT, FlorisImeSizing.Static.smartbarHeightPx)
|
||||
val latch = CountDownLatch(rawSuggestions.size)
|
||||
val suggestionsArray = Array<NlpInlineAutofillSuggestion?>(rawSuggestions.size) { null }
|
||||
|
||||
flogInfo { "showInlineSuggestions: [${sequenceId}] start inflating suggestions" }
|
||||
for ((index, rawSuggestion) in rawSuggestions.withIndex()) {
|
||||
rawSuggestion.inflate(context, size, context.mainExecutor) { view ->
|
||||
suggestionsArray[index] = NlpInlineAutofillSuggestion(rawSuggestion.info, view)
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
if (!latch.await(2_000, TimeUnit.MILLISECONDS)) {
|
||||
flogWarning { "showInlineSuggestions: [${sequenceId}] timed out while waiting for all " +
|
||||
"suggestions to inflate" }
|
||||
return@launch
|
||||
}
|
||||
|
||||
val suggestions = suggestionsArray.filterNotNull().sortedByDescending { it.info.isPinned }
|
||||
setterGuard.lock()
|
||||
flogInfo { "showInlineSuggestions: [${sequenceId}] successfully inflated " +
|
||||
"${suggestions.count { it.view != null }} out of ${suggestions.size} suggestions" }
|
||||
if (currentSequenceId.get() == sequenceId) {
|
||||
flogInfo { "showInlineSuggestions: [${sequenceId}] setting suggestions" }
|
||||
_suggestions.value = suggestions
|
||||
} else {
|
||||
flogWarning { "showInlineSuggestions: [${sequenceId}] seqId != current, skip setting suggestions" }
|
||||
}
|
||||
setterGuard.unlock()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun clearInlineSuggestions() {
|
||||
// Increment sequence id to invalidate eventual pending suggestions
|
||||
clearInlineSuggestions(generateSequenceId())
|
||||
}
|
||||
|
||||
private fun clearInlineSuggestions(sequenceId: Int) {
|
||||
scope.launch {
|
||||
setterGuard.lock()
|
||||
flogInfo { "clearInlineSuggestions: [${sequenceId}] clearing suggestions" }
|
||||
_suggestions.value = emptyList()
|
||||
setterGuard.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateSequenceId(): Int {
|
||||
return currentSequenceId.incrementAndGet()
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,8 @@
|
||||
package dev.patrickgold.florisboard.ime.nlp
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import android.util.LruCache
|
||||
import android.util.Size
|
||||
import android.view.inputmethod.InlineSuggestion
|
||||
import android.widget.inline.InlineContentView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
@@ -34,12 +28,10 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorContent
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorRange
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EMOJI_SUGGESTION_MAX_COUNT
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.han.HanShapeBasedLanguageProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.latin.LatinLanguageProvider
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -53,7 +45,6 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.properties.Delegates
|
||||
@@ -70,7 +61,7 @@ class NlpManager(context: Context) {
|
||||
private val subtypeManager by context.subtypeManager()
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
private val clipboardSuggestionProvider = ClipboardSuggestionProvider()
|
||||
private val clipboardSuggestionProvider = ClipboardSuggestionProvider(context)
|
||||
private val emojiSuggestionProvider = EmojiSuggestionProvider(context)
|
||||
private val providers = guardedByLock {
|
||||
mapOf(
|
||||
@@ -94,10 +85,6 @@ class NlpManager(context: Context) {
|
||||
_activeCandidatesFlow.value = v
|
||||
}
|
||||
|
||||
private val inlineContentViews = Collections.synchronizedMap<InlineSuggestion, InlineContentView>(hashMapOf())
|
||||
private val _inlineSuggestions = MutableLiveData<List<InlineSuggestion>>(emptyList())
|
||||
val inlineSuggestions: LiveData<List<InlineSuggestion>> get() = _inlineSuggestions
|
||||
|
||||
val debugOverlaySuggestionsInfos = LruCache<Long, Pair<String, SpellingResult>>(10)
|
||||
var debugOverlayVersion = MutableLiveData(0)
|
||||
private val debugOverlayVersionSource = AtomicInteger(0)
|
||||
@@ -112,6 +99,9 @@ class NlpManager(context: Context) {
|
||||
prefs.suggestion.clipboardContentEnabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.emoji.suggestionEnabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) { subtype ->
|
||||
preload(subtype)
|
||||
}
|
||||
@@ -202,21 +192,45 @@ class NlpManager(context: Context) {
|
||||
}
|
||||
|
||||
fun isSuggestionOn(): Boolean =
|
||||
prefs.suggestion.enabled.get() || providerForcesSuggestionOn(subtypeManager.activeSubtype)
|
||||
prefs.suggestion.enabled.get()
|
||||
|| prefs.emoji.suggestionEnabled.get()
|
||||
|| providerForcesSuggestionOn(subtypeManager.activeSubtype)
|
||||
|
||||
fun suggest(subtype: Subtype, content: EditorContent) {
|
||||
val reqTime = SystemClock.uptimeMillis()
|
||||
scope.launch {
|
||||
val suggestions = getSuggestionProvider(subtype).suggest(
|
||||
subtype = subtype,
|
||||
content = content,
|
||||
maxCandidateCount = 8,
|
||||
allowPossiblyOffensive = !prefs.suggestion.blockPossiblyOffensive.get(),
|
||||
isPrivateSession = keyboardManager.activeState.isIncognitoMode,
|
||||
)
|
||||
val emojiSuggestions = when {
|
||||
prefs.emoji.suggestionEnabled.get() -> {
|
||||
emojiSuggestionProvider.suggest(
|
||||
subtype = subtype,
|
||||
content = content,
|
||||
maxCandidateCount = prefs.emoji.suggestionCandidateMaxCount.get(),
|
||||
allowPossiblyOffensive = !prefs.suggestion.blockPossiblyOffensive.get(),
|
||||
isPrivateSession = keyboardManager.activeState.isIncognitoMode,
|
||||
)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
val suggestions = when {
|
||||
emojiSuggestions.isNotEmpty() && prefs.emoji.suggestionType.get().prefix.isNotEmpty() -> {
|
||||
emptyList()
|
||||
}
|
||||
else -> {
|
||||
getSuggestionProvider(subtype).suggest(
|
||||
subtype = subtype,
|
||||
content = content,
|
||||
maxCandidateCount = 8,
|
||||
allowPossiblyOffensive = !prefs.suggestion.blockPossiblyOffensive.get(),
|
||||
isPrivateSession = keyboardManager.activeState.isIncognitoMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
internalSuggestionsGuard.withLock {
|
||||
if (internalSuggestions.first < reqTime) {
|
||||
internalSuggestions = reqTime to suggestions
|
||||
internalSuggestions = reqTime to buildList {
|
||||
addAll(emojiSuggestions)
|
||||
addAll(suggestions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,24 +281,16 @@ class NlpManager(context: Context) {
|
||||
runBlocking {
|
||||
val candidates = when {
|
||||
isSuggestionOn() -> {
|
||||
emojiSuggestionProvider.suggest(
|
||||
subtype = subtypeManager.activeSubtype,
|
||||
clipboardSuggestionProvider.suggest(
|
||||
subtype = Subtype.DEFAULT,
|
||||
content = editorInstance.activeContent,
|
||||
maxCandidateCount = EMOJI_SUGGESTION_MAX_COUNT,
|
||||
maxCandidateCount = 8,
|
||||
allowPossiblyOffensive = !prefs.suggestion.blockPossiblyOffensive.get(),
|
||||
isPrivateSession = keyboardManager.activeState.isIncognitoMode,
|
||||
).ifEmpty {
|
||||
clipboardSuggestionProvider.suggest(
|
||||
subtype = Subtype.DEFAULT,
|
||||
content = editorInstance.activeContent,
|
||||
maxCandidateCount = 8,
|
||||
allowPossiblyOffensive = !prefs.suggestion.blockPossiblyOffensive.get(),
|
||||
isPrivateSession = keyboardManager.activeState.isIncognitoMode,
|
||||
).ifEmpty {
|
||||
buildList {
|
||||
internalSuggestionsGuard.withLock {
|
||||
addAll(internalSuggestions.second)
|
||||
}
|
||||
buildList {
|
||||
internalSuggestionsGuard.withLock {
|
||||
addAll(internalSuggestions.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,68 +298,27 @@ class NlpManager(context: Context) {
|
||||
else -> emptyList()
|
||||
}
|
||||
activeCandidates = candidates
|
||||
autoExpandCollapseSmartbarActions(candidates, inlineSuggestions.value)
|
||||
autoExpandCollapseSmartbarActions(candidates, NlpInlineAutofill.suggestions.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the given inline suggestions. Once all provided views are ready, the suggestions
|
||||
* strip is updated and the Smartbar update cycle is triggered.
|
||||
*
|
||||
* @param inlineSuggestions A collection of inline suggestions to be inflated and shown.
|
||||
*/
|
||||
fun showInlineSuggestions(inlineSuggestions: List<InlineSuggestion>) {
|
||||
inlineContentViews.clear()
|
||||
_inlineSuggestions.postValue(inlineSuggestions)
|
||||
autoExpandCollapseSmartbarActions(activeCandidates, inlineSuggestions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the inline suggestions and triggers the Smartbar update cycle.
|
||||
*/
|
||||
fun clearInlineSuggestions() {
|
||||
inlineContentViews.clear()
|
||||
_inlineSuggestions.postValue(emptyList())
|
||||
autoExpandCollapseSmartbarActions(activeCandidates, null)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun inflateOrGet(
|
||||
context: Context,
|
||||
size: Size,
|
||||
inlineSuggestion: InlineSuggestion,
|
||||
callback: (InlineContentView) -> Unit,
|
||||
) {
|
||||
val view = inlineContentViews[inlineSuggestion]
|
||||
if (view != null) {
|
||||
callback(view)
|
||||
} else {
|
||||
try {
|
||||
inlineSuggestion.inflate(context, size, context.mainExecutor) { inflatedView ->
|
||||
if (inflatedView != null) {
|
||||
inlineContentViews[inlineSuggestion] = inflatedView
|
||||
callback(inflatedView)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
flogError { e.toString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoExpandCollapseSmartbarActions(list1: List<*>?, list2: List<*>?) {
|
||||
if (prefs.smartbar.enabled.get() && prefs.smartbar.sharedActionsAutoExpandCollapse.get()) {
|
||||
if (keyboardManager.inputEventDispatcher.isRepeatableCodeLastDown()
|
||||
|| keyboardManager.activeState.isActionsOverflowVisible
|
||||
) {
|
||||
return // We do not auto switch if a repeatable action key was last pressed or if the actions overflow
|
||||
// menu is visible to prevent annoying UI changes
|
||||
}
|
||||
val isSelection = editorInstance.activeContent.selection.isSelectionMode
|
||||
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
fun autoExpandCollapseSmartbarActions(list1: List<*>?, list2: List<*>?) {
|
||||
if (!prefs.smartbar.enabled.get()) {// || !prefs.smartbar.sharedActionsAutoExpandCollapse.get()) {
|
||||
return
|
||||
}
|
||||
// TODO: this is a mess and needs to be cleaned up in v0.5 with the NLP development
|
||||
/*if (keyboardManager.inputEventDispatcher.isRepeatableCodeLastDown()
|
||||
&& !keyboardManager.inputEventDispatcher.isPressed(KeyCode.DELETE)
|
||||
&& !keyboardManager.inputEventDispatcher.isPressed(KeyCode.FORWARD_DELETE)
|
||||
|| keyboardManager.activeState.isActionsOverflowVisible
|
||||
) {
|
||||
return // We do not auto switch if a repeatable action key was last pressed or if the actions overflow
|
||||
// menu is visible to prevent annoying UI changes
|
||||
}*/
|
||||
val isSelection = editorInstance.activeContent.selection.isSelectionMode
|
||||
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
}
|
||||
|
||||
fun addToDebugOverlay(word: String, info: SpellingResult) {
|
||||
@@ -384,7 +349,7 @@ class NlpManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
inner class ClipboardSuggestionProvider internal constructor() : SuggestionProvider {
|
||||
inner class ClipboardSuggestionProvider internal constructor(private val context: Context) : SuggestionProvider {
|
||||
private var lastClipboardItemId: Long = -1
|
||||
|
||||
override val providerId = "org.florisboard.nlp.providers.clipboard"
|
||||
@@ -413,7 +378,10 @@ class NlpManager(context: Context) {
|
||||
return buildList {
|
||||
val now = System.currentTimeMillis()
|
||||
if ((now - currentItem.creationTimestampMs) < prefs.suggestion.clipboardContentTimeout.get() * 1000) {
|
||||
add(ClipboardSuggestionCandidate(currentItem, sourceProvider = this@ClipboardSuggestionProvider))
|
||||
add(ClipboardSuggestionCandidate(currentItem, sourceProvider = this@ClipboardSuggestionProvider, context = context))
|
||||
if (currentItem.isSensitive) {
|
||||
return@buildList
|
||||
}
|
||||
if (currentItem.type == ItemType.TEXT) {
|
||||
val text = currentItem.stringRepresentation()
|
||||
val matches = buildList {
|
||||
@@ -437,6 +405,7 @@ class NlpManager(context: Context) {
|
||||
}
|
||||
),
|
||||
sourceProvider = this@ClipboardSuggestionProvider,
|
||||
context = context,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.icu.text.BreakIterator
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorContent
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorRange
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EMOJI_SUGGESTION_INDICATOR
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
|
||||
/**
|
||||
* Base interface for any NLP provider implementation. NLP providers maintain their own internal state and only receive
|
||||
@@ -221,7 +221,7 @@ interface SuggestionProvider : NlpProvider {
|
||||
// Include Emoji indicator in local composing. This is required so that emoji suggestion indicator'
|
||||
// can be detected in the composing text.
|
||||
(pos - 1).takeIf { updatedPos ->
|
||||
textBeforeSelection.getOrNull(updatedPos) == EMOJI_SUGGESTION_INDICATOR
|
||||
textBeforeSelection.getOrNull(updatedPos) == EmojiSuggestionType.LEADING_COLON.prefix.first()
|
||||
} ?: pos
|
||||
}
|
||||
EditorRange(start, end)
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.nlp
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Assignment
|
||||
import androidx.compose.material.icons.filled.Email
|
||||
import androidx.compose.material.icons.filled.Image
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.Videocam
|
||||
import androidx.compose.material.icons.outlined.Assignment
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
@@ -123,8 +124,9 @@ data class WordSuggestionCandidate(
|
||||
data class ClipboardSuggestionCandidate(
|
||||
val clipboardItem: ClipboardItem,
|
||||
override val sourceProvider: SuggestionProvider?,
|
||||
val context: Context,
|
||||
) : SuggestionCandidate {
|
||||
override val text: CharSequence = clipboardItem.stringRepresentation()
|
||||
override val text: CharSequence = clipboardItem.displayText(context)
|
||||
|
||||
override val secondaryText: CharSequence? = null
|
||||
|
||||
@@ -139,7 +141,7 @@ data class ClipboardSuggestionCandidate(
|
||||
NetworkUtils.isEmailAddress(text) -> Icons.Default.Email
|
||||
NetworkUtils.isUrl(text) -> Icons.Default.Link
|
||||
NetworkUtils.isPhoneNumber(text) -> Icons.Default.Phone
|
||||
else -> Icons.Outlined.Assignment
|
||||
else -> Icons.AutoMirrored.Outlined.Assignment
|
||||
}
|
||||
ItemType.IMAGE -> Icons.Default.Image
|
||||
ItemType.VIDEO -> Icons.Default.Videocam
|
||||
@@ -157,6 +159,7 @@ data class ClipboardSuggestionCandidate(
|
||||
*/
|
||||
data class EmojiSuggestionCandidate(
|
||||
val emoji: Emoji,
|
||||
val showName: Boolean,
|
||||
override val confidence: Double = 1.0,
|
||||
override val isEligibleForAutoCommit: Boolean = false,
|
||||
override val isEligibleForUserRemoval: Boolean = false,
|
||||
@@ -164,5 +167,5 @@ data class EmojiSuggestionCandidate(
|
||||
override val sourceProvider: SuggestionProvider? = null,
|
||||
) : SuggestionCandidate {
|
||||
override val text = emoji.value
|
||||
override val secondaryText = emoji.name
|
||||
override val secondaryText = if (showName) emoji.name else null
|
||||
}
|
||||
|
||||
@@ -24,14 +24,13 @@ import dev.patrickgold.florisboard.ime.nlp.SpellingProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingResult
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import org.florisboard.lib.android.readText
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.android.readText
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
|
||||
class LatinLanguageProvider(context: Context) : SpellingProvider, SuggestionProvider {
|
||||
@@ -106,7 +105,8 @@ class LatinLanguageProvider(context: Context) : SpellingProvider, SuggestionProv
|
||||
allowPossiblyOffensive: Boolean,
|
||||
isPrivateSession: Boolean,
|
||||
): List<SuggestionCandidate> {
|
||||
val word = content.composingText.ifBlank { "next" }
|
||||
return emptyList()
|
||||
/*val word = content.composingText.ifBlank { "next" }
|
||||
val suggestions = buildList {
|
||||
for (n in 0 until maxCandidateCount) {
|
||||
add(WordSuggestionCandidate(
|
||||
@@ -119,7 +119,7 @@ class LatinLanguageProvider(context: Context) : SpellingProvider, SuggestionProv
|
||||
))
|
||||
}
|
||||
}
|
||||
return suggestions
|
||||
return suggestions*/
|
||||
}
|
||||
|
||||
override suspend fun notifySuggestionAccepted(subtype: Subtype, candidate: SuggestionCandidate) {
|
||||
|
||||
@@ -22,8 +22,8 @@ import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ZoomOutMap
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -81,9 +81,9 @@ fun RowScope.OneHandedPanel(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (panelSide == OneHandedMode.START) {
|
||||
Icons.Default.KeyboardArrowLeft
|
||||
Icons.AutoMirrored.Filled.KeyboardArrowLeft
|
||||
} else {
|
||||
Icons.Default.KeyboardArrowRight
|
||||
Icons.AutoMirrored.Filled.KeyboardArrowRight
|
||||
},
|
||||
contentDescription = stringRes(
|
||||
if (panelSide == OneHandedMode.START) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -58,16 +58,14 @@ import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.conditional
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
private val CandidatesRowScrollbarHeight = 2.dp
|
||||
val CandidatesRowScrollbarHeight = 2.dp
|
||||
|
||||
@Composable
|
||||
fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
@@ -79,84 +77,71 @@ fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
|
||||
val displayMode by prefs.suggestion.displayMode.observeAsState()
|
||||
val candidates by nlpManager.activeCandidatesFlow.collectAsState()
|
||||
val inlineSuggestions by nlpManager.inlineSuggestions.observeAsNonNullState()
|
||||
|
||||
val rowStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarCandidatesRow)
|
||||
val spacerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarCandidateSpacer)
|
||||
|
||||
if (AndroidVersion.ATLEAST_API30_R && inlineSuggestions.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.florisHorizontalScroll(scrollbarHeight = CandidatesRowScrollbarHeight),
|
||||
) {
|
||||
for (inlineSuggestion in inlineSuggestions) {
|
||||
InlineSuggestionView(inlineSuggestion = inlineSuggestion)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.snyggBackground(context, rowStyle)
|
||||
.conditional(displayMode == CandidatesDisplayMode.DYNAMIC_SCROLLABLE && candidates.size > 1) {
|
||||
florisHorizontalScroll(scrollbarHeight = CandidatesRowScrollbarHeight)
|
||||
},
|
||||
horizontalArrangement = if (candidates.size > 1) {
|
||||
Arrangement.Start
|
||||
} else {
|
||||
Arrangement.Center
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.snyggBackground(context, rowStyle)
|
||||
.conditional(displayMode == CandidatesDisplayMode.DYNAMIC_SCROLLABLE && candidates.size > 1) {
|
||||
florisHorizontalScroll(scrollbarHeight = CandidatesRowScrollbarHeight)
|
||||
},
|
||||
) {
|
||||
if (candidates.isNotEmpty()) {
|
||||
val candidateModifier = if (candidates.size == 1) {
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f, fill = false)
|
||||
} else {
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.conditional(displayMode == CandidatesDisplayMode.CLASSIC) {
|
||||
weight(1f)
|
||||
}
|
||||
.conditional(displayMode != CandidatesDisplayMode.CLASSIC) {
|
||||
wrapContentWidth().widthIn(max = 160.dp)
|
||||
}
|
||||
}
|
||||
val list = when (displayMode) {
|
||||
CandidatesDisplayMode.CLASSIC -> candidates.subList(0, 3.coerceAtMost(candidates.size))
|
||||
else -> candidates
|
||||
}
|
||||
for ((n, candidate) in list.withIndex()) {
|
||||
if (n > 0) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.fillMaxHeight(0.6f)
|
||||
.align(Alignment.CenterVertically)
|
||||
.snyggBackground(context, spacerStyle),
|
||||
)
|
||||
horizontalArrangement = if (candidates.size > 1) {
|
||||
Arrangement.Start
|
||||
} else {
|
||||
Arrangement.Center
|
||||
},
|
||||
) {
|
||||
if (candidates.isNotEmpty()) {
|
||||
val candidateModifier = if (candidates.size == 1) {
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f, fill = false)
|
||||
} else {
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.conditional(displayMode == CandidatesDisplayMode.CLASSIC) {
|
||||
weight(1f)
|
||||
}
|
||||
CandidateItem(
|
||||
modifier = candidateModifier,
|
||||
candidate = candidate,
|
||||
displayMode = displayMode,
|
||||
onClick = {
|
||||
// Can't use candidate directly
|
||||
keyboardManager.commitCandidate(candidates[n])
|
||||
},
|
||||
onLongPress = {
|
||||
// Can't use candidate directly
|
||||
val candidateItem = candidates[n]
|
||||
if (candidateItem.isEligibleForUserRemoval) {
|
||||
nlpManager.removeSuggestion(subtypeManager.activeSubtype, candidateItem)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
longPressDelay = prefs.keyboard.longPressDelay.get().toLong(),
|
||||
.conditional(displayMode != CandidatesDisplayMode.CLASSIC) {
|
||||
wrapContentWidth().widthIn(max = 160.dp)
|
||||
}
|
||||
}
|
||||
val list = when (displayMode) {
|
||||
CandidatesDisplayMode.CLASSIC -> candidates.subList(0, 3.coerceAtMost(candidates.size))
|
||||
else -> candidates
|
||||
}
|
||||
for ((n, candidate) in list.withIndex()) {
|
||||
if (n > 0) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.fillMaxHeight(0.6f)
|
||||
.align(Alignment.CenterVertically)
|
||||
.snyggBackground(context, spacerStyle),
|
||||
)
|
||||
}
|
||||
CandidateItem(
|
||||
modifier = candidateModifier,
|
||||
candidate = candidate,
|
||||
displayMode = displayMode,
|
||||
onClick = {
|
||||
// Can't use candidate directly
|
||||
keyboardManager.commitCandidate(candidates[n])
|
||||
},
|
||||
onLongPress = {
|
||||
// Can't use candidate directly
|
||||
val candidateItem = candidates[n]
|
||||
if (candidateItem.isEligibleForUserRemoval) {
|
||||
nlpManager.removeSuggestion(subtypeManager.activeSubtype, candidateItem)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
longPressDelay = prefs.keyboard.longPressDelay.get().toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +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.ime.smartbar
|
||||
|
||||
import android.os.Build
|
||||
import android.util.Size
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InlineSuggestion
|
||||
import android.widget.inline.InlineContentView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@Composable
|
||||
fun InlineSuggestionView(inlineSuggestion: InlineSuggestion) = with(LocalDensity.current) {
|
||||
val context = LocalContext.current
|
||||
val nlpManager by context.nlpManager()
|
||||
|
||||
val size = Size(ViewGroup.LayoutParams.WRAP_CONTENT, FlorisImeSizing.smartbarHeight.toPx().toInt())
|
||||
var inlineContentView by remember { mutableStateOf<InlineContentView?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
nlpManager.inflateOrGet(context, size, inlineSuggestion) { view ->
|
||||
inlineContentView = view
|
||||
}
|
||||
}
|
||||
|
||||
if (inlineContentView != null) {
|
||||
AndroidView(
|
||||
factory = { inlineContentView!! },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInParent
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofillSuggestion
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@Composable
|
||||
fun InlineSuggestionsUi(
|
||||
inlineSuggestions: List<NlpInlineAutofillSuggestion>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val almostEmptyRect = remember { android.graphics.Rect(0, 0, 1, 1) }
|
||||
|
||||
Row(
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.florisHorizontalScroll(
|
||||
state = scrollState,
|
||||
scrollbarHeight = CandidatesRowScrollbarHeight,
|
||||
),
|
||||
) {
|
||||
val xMin = scrollState.value
|
||||
val xMax = scrollState.value + scrollState.viewportSize
|
||||
for (inlineSuggestion in inlineSuggestions) {
|
||||
if (inlineSuggestion.view == null) {
|
||||
continue
|
||||
}
|
||||
var chipPos by remember { mutableStateOf(IntOffset.Zero) }
|
||||
AndroidView(
|
||||
modifier = Modifier.onGloballyPositioned { chipPos = it.positionInParent().toIntOffset() },
|
||||
factory = { inlineSuggestion.view },
|
||||
update = { view ->
|
||||
view.clipBounds = android.graphics.Rect(
|
||||
(xMin - chipPos.x).coerceAtLeast(0),
|
||||
0,
|
||||
(xMax - chipPos.x).coerceAtMost(view.width),
|
||||
view.height,
|
||||
)
|
||||
// The empty rect is a workaround for a bug (I suppose) where an empty rect causes
|
||||
// no clipping, but we actually want to completely hide the view.
|
||||
// Thus we just show the topmost pixel of the view, which due to the round shape
|
||||
// of the theme should be transparent anyways.
|
||||
if (view.clipBounds.isEmpty) {
|
||||
view.clipBounds = almostEmptyRect
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import androidx.compose.material.icons.filled.UnfoldMore
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -56,6 +57,7 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionsRow
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.ToggleOverflowPanelAction
|
||||
@@ -64,8 +66,10 @@ import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.horizontalTween
|
||||
import dev.patrickgold.florisboard.lib.compose.verticalTween
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
@@ -138,6 +142,13 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val nlpManager by context.nlpManager()
|
||||
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
LaunchedEffect(inlineSuggestions) {
|
||||
nlpManager.autoExpandCollapseSmartbarActions(null, inlineSuggestions)
|
||||
}
|
||||
val shouldShowInlineSuggestionsUi = AndroidVersion.ATLEAST_API30_R && inlineSuggestions.isNotEmpty()
|
||||
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val flipToggles by prefs.smartbar.flipToggles.observeAsState()
|
||||
@@ -223,7 +234,11 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
enter = enterTransition,
|
||||
exit = exitTransition,
|
||||
) {
|
||||
CandidatesRow()
|
||||
if (shouldShowInlineSuggestionsUi) {
|
||||
InlineSuggestionsUi(inlineSuggestions)
|
||||
} else {
|
||||
CandidatesRow()
|
||||
}
|
||||
}
|
||||
androidx.compose.animation.AnimatedVisibility(
|
||||
visible = expanded,
|
||||
@@ -331,11 +346,19 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
) {
|
||||
when (smartbarLayout) {
|
||||
SmartbarLayout.SUGGESTIONS_ONLY -> {
|
||||
CandidatesRow()
|
||||
if (shouldShowInlineSuggestionsUi) {
|
||||
InlineSuggestionsUi(inlineSuggestions)
|
||||
} else {
|
||||
CandidatesRow()
|
||||
}
|
||||
}
|
||||
|
||||
SmartbarLayout.ACTIONS_ONLY -> {
|
||||
QuickActionsRow(elementName = FlorisImeUi.SmartbarSharedActionsRow)
|
||||
if (shouldShowInlineSuggestionsUi) {
|
||||
InlineSuggestionsUi(inlineSuggestions)
|
||||
} else {
|
||||
QuickActionsRow(elementName = FlorisImeUi.SmartbarSharedActionsRow)
|
||||
}
|
||||
}
|
||||
|
||||
SmartbarLayout.SUGGESTIONS_ACTIONS_SHARED -> {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -33,7 +32,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -76,7 +75,6 @@ private const val ItemNotFound = -1
|
||||
private val NoopAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.NOOP))
|
||||
private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.DRAG_MARKER))
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
@@ -105,17 +103,20 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorHeader)
|
||||
val subheaderStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorSubheader)
|
||||
|
||||
fun findItemForOffset(offset: IntOffset): LazyGridItemInfo? {
|
||||
fun findItemForOffsetOrClosestInRow(offset: IntOffset): LazyGridItemInfo? {
|
||||
var closestItemInRow: LazyGridItemInfo? = null
|
||||
// Using manual for loop with indices instead of firstOrNull() because this method gets
|
||||
// called a lot and firstOrNull allocates an iterator for each call
|
||||
for (index in gridState.layoutInfo.visibleItemsInfo.indices) {
|
||||
val item = gridState.layoutInfo.visibleItemsInfo[index]
|
||||
if (offset.x in item.offset.x..(item.offset.x + item.size.width) &&
|
||||
offset.y in item.offset.y..(item.offset.y + item.size.height)) {
|
||||
return item
|
||||
if (offset.y in item.offset.y..(item.offset.y + item.size.height)) {
|
||||
if (offset.x in item.offset.x..(item.offset.x + item.size.width)) {
|
||||
return item
|
||||
}
|
||||
closestItemInRow = item
|
||||
}
|
||||
}
|
||||
return null
|
||||
return closestItemInRow
|
||||
}
|
||||
|
||||
fun indexOfStickyAction(item: LazyGridItemInfo): Int {
|
||||
@@ -158,7 +159,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
}
|
||||
|
||||
fun beginDragGesture(pos: IntOffset) {
|
||||
val item = findItemForOffset(pos) ?: return
|
||||
val item = findItemForOffsetOrClosestInRow(pos) ?: return
|
||||
val stickyActionIndex = indexOfStickyAction(item)
|
||||
val dynamicActionIndex = indexOfDynamicAction(item)
|
||||
val hiddenActionIndex = indexOfHiddenAction(item)
|
||||
@@ -182,7 +183,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
if (activeDragAction == null) return
|
||||
val pos = activeDragPosition + posChange
|
||||
activeDragPosition = pos
|
||||
val item = findItemForOffset(pos) ?: return
|
||||
val item = findItemForOffsetOrClosestInRow(pos) ?: return
|
||||
val stickyActionIndex = indexOfStickyAction(item)
|
||||
val dynamicActionIndex = indexOfDynamicAction(item)
|
||||
val hiddenActionIndex = indexOfHiddenAction(item)
|
||||
@@ -263,7 +264,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
onClick = {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
},
|
||||
icon = Icons.Default.KeyboardArrowLeft,
|
||||
icon = Icons.AutoMirrored.Filled.KeyboardArrowLeft,
|
||||
iconColor = headerStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
)
|
||||
Text(
|
||||
@@ -299,7 +300,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
}
|
||||
item(key = keyOf(stickyAction)) {
|
||||
QuickActionButton(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
modifier = Modifier.animateItem(),
|
||||
action = stickyAction,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
@@ -314,7 +315,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
}
|
||||
itemsIndexed(dynamicActions, key = { i, a -> keyOf(a) ?: i }) { _, action ->
|
||||
QuickActionButton(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
modifier = Modifier.animateItem(),
|
||||
action = action,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
@@ -329,7 +330,7 @@ fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
}
|
||||
itemsIndexed(hiddenActions, key = { i, a -> keyOf(a) ?: i }) { _, action ->
|
||||
QuickActionButton(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
modifier = Modifier.animateItem(),
|
||||
action = action,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
|
||||
@@ -19,7 +19,6 @@ package dev.patrickgold.florisboard.ime.text
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -32,7 +31,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
@@ -74,7 +72,7 @@ fun TextInputLayout(
|
||||
val indicatorStyle = FlorisImeTheme.style.get(FlorisImeUi.IncognitoModeIndicator)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(192.dp)
|
||||
.matchParentSize()
|
||||
.align(Alignment.Center),
|
||||
painter = painterResource(R.drawable.ic_incognito),
|
||||
contentDescription = null,
|
||||
|
||||
@@ -220,31 +220,39 @@ fun TextKeyboardLayout(
|
||||
}
|
||||
},
|
||||
) {
|
||||
val keyMarginH by prefs.keyboard.keySpacingHorizontal.observeAsTransformingState { it.dp.toPx() }
|
||||
val keyMarginV by prefs.keyboard.keySpacingVertical.observeAsTransformingState { it.dp.toPx() }
|
||||
val desiredKey = remember { TextKey(data = TextKeyData.UNSPECIFIED) }
|
||||
val keyboardWidth = constraints.maxWidth.toFloat()
|
||||
val keyboardHeight = constraints.maxHeight.toFloat()
|
||||
desiredKey.touchBounds.apply {
|
||||
if (isSmartbarKeyboard) {
|
||||
width = keyboardWidth / 8f
|
||||
height = FlorisImeSizing.smartbarHeight.toPx()
|
||||
} else {
|
||||
width = keyboardWidth / 10f
|
||||
height = when (keyboard.mode) {
|
||||
KeyboardMode.CHARACTERS,
|
||||
KeyboardMode.NUMERIC_ADVANCED,
|
||||
KeyboardMode.SYMBOLS,
|
||||
KeyboardMode.SYMBOLS2 -> {
|
||||
(FlorisImeSizing.keyboardUiHeight() / keyboard.rowCount)
|
||||
.coerceAtMost(FlorisImeSizing.keyboardRowBaseHeight * 1.12f).toPx()
|
||||
val keyMarginH by prefs.keyboard.keySpacingHorizontal.observeAsTransformingState { it.dp.toPx() }
|
||||
val keyMarginV by prefs.keyboard.keySpacingVertical.observeAsTransformingState { it.dp.toPx() }
|
||||
val keyboardRowBaseHeight = FlorisImeSizing.keyboardRowBaseHeight
|
||||
|
||||
val desiredKey = remember(
|
||||
keyboard, isSmartbarKeyboard, keyboardWidth, keyboardHeight, keyMarginH, keyMarginV,
|
||||
keyboardRowBaseHeight
|
||||
) {
|
||||
TextKey(data = TextKeyData.UNSPECIFIED).also { desiredKey ->
|
||||
desiredKey.touchBounds.apply {
|
||||
if (isSmartbarKeyboard) {
|
||||
width = keyboardWidth / 8f
|
||||
height = keyboardHeight
|
||||
} else {
|
||||
width = keyboardWidth / 10f
|
||||
height = when (keyboard.mode) {
|
||||
KeyboardMode.CHARACTERS,
|
||||
KeyboardMode.NUMERIC_ADVANCED,
|
||||
KeyboardMode.SYMBOLS,
|
||||
KeyboardMode.SYMBOLS2 -> {
|
||||
(keyboardHeight / keyboard.rowCount)
|
||||
.coerceAtMost(keyboardRowBaseHeight.toPx() * 1.12f)
|
||||
}
|
||||
else -> keyboardRowBaseHeight.toPx()
|
||||
}
|
||||
}
|
||||
else -> FlorisImeSizing.keyboardRowBaseHeight.toPx()
|
||||
}
|
||||
desiredKey.visibleBounds.applyFrom(desiredKey.touchBounds).deflateBy(keyMarginH, keyMarginV)
|
||||
keyboard.layout(keyboardWidth, keyboardHeight, desiredKey, !isSmartbarKeyboard)
|
||||
}
|
||||
}
|
||||
desiredKey.visibleBounds.applyFrom(desiredKey.touchBounds).deflateBy(keyMarginH, keyMarginV)
|
||||
keyboard.layout(keyboardWidth, keyboardHeight, desiredKey, !isSmartbarKeyboard)
|
||||
|
||||
val fontSizeMultiplier = prefs.keyboard.fontSizeMultiplier()
|
||||
val popupUiController = rememberPopupUiController(
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.theme
|
||||
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionEditor
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
@@ -41,14 +43,14 @@ class ThemeExtension(
|
||||
override fun edit() = ThemeExtensionEditor(
|
||||
meta = meta,
|
||||
dependencies = dependencies?.toMutableList() ?: mutableListOf(),
|
||||
themes = themes.map { it.edit() }.toMutableList(),
|
||||
themes = mutableStateListOf(*themes.map { it.edit() }.toTypedArray()),
|
||||
)
|
||||
}
|
||||
|
||||
class ThemeExtensionEditor(
|
||||
override var meta: ExtensionMeta,
|
||||
override val dependencies: MutableList<String>,
|
||||
val themes: MutableList<ThemeExtensionComponentEditor>,
|
||||
val themes: SnapshotStateList<ThemeExtensionComponentEditor>,
|
||||
) : ExtensionEditor {
|
||||
|
||||
override fun build() = ThemeExtension(
|
||||
|
||||
@@ -199,70 +199,68 @@ class ThemeManager(context: Context) {
|
||||
context: Context,
|
||||
style: SnyggStylesheet = activeThemeInfo.value?.stylesheet ?: FlorisImeThemeBaseStyle,
|
||||
): Bundle {
|
||||
val chipStyle = style.getStatic(FlorisImeUi.SmartbarSharedActionsToggle)
|
||||
val bgColor = chipStyle.background.solidColor(context)
|
||||
val fgColor = chipStyle.foreground.solidColor(context)
|
||||
val snyggStyle = style.getStatic(FlorisImeUi.SmartbarSharedActionsToggle)
|
||||
val bgColor = snyggStyle.background.solidColor(context)
|
||||
val fgColor = snyggStyle.foreground.solidColor(context)
|
||||
|
||||
val bgDrawableId = androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background
|
||||
val stylesBuilder = UiVersions.newStylesBuilder()
|
||||
val suggestionStyle = InlineSuggestionUi.newStyleBuilder()
|
||||
.setSingleIconChipStyle(
|
||||
ViewStyle.Builder()
|
||||
.setBackground(
|
||||
Icon.createWithResource(context, bgDrawableId).setTint(bgColor.toArgb())
|
||||
)
|
||||
.setPadding(0, 0, 0, 0)
|
||||
.build()
|
||||
val bgDrawable = Icon.createWithResource(context, bgDrawableId).apply {
|
||||
setTint(bgColor.toArgb())
|
||||
}
|
||||
val singleIconChipStyle = ViewStyle.Builder().run {
|
||||
setBackground(bgDrawable)
|
||||
setPadding(0, 0, 0, 0)
|
||||
build()
|
||||
}
|
||||
val chipStyle = ViewStyle.Builder().run {
|
||||
setBackground(bgDrawable)
|
||||
setPadding(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_bottom).toInt(),
|
||||
)
|
||||
.setChipStyle(
|
||||
ViewStyle.Builder()
|
||||
.setBackground(
|
||||
Icon.createWithResource(context, bgDrawableId).setTint(bgColor.toArgb())
|
||||
)
|
||||
.setPadding(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_bg_padding_bottom).toInt(),
|
||||
)
|
||||
.build()
|
||||
build()
|
||||
}
|
||||
val iconStyle = ImageViewStyle.Builder().run {
|
||||
setLayoutMargin(0, 0, 0, 0)
|
||||
build()
|
||||
}
|
||||
val titleStyle = TextViewStyle.Builder().run {
|
||||
setLayoutMargin(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_bottom).toInt(),
|
||||
)
|
||||
.setStartIconStyle(
|
||||
ImageViewStyle.Builder()
|
||||
.setLayoutMargin(0, 0, 0, 0)
|
||||
.build()
|
||||
setTextColor(fgColor.toArgb())
|
||||
setTextSize(16f)
|
||||
build()
|
||||
}
|
||||
val subtitleStyle = TextViewStyle.Builder().run {
|
||||
setLayoutMargin(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_bottom).toInt(),
|
||||
)
|
||||
.setTitleStyle(
|
||||
TextViewStyle.Builder()
|
||||
.setLayoutMargin(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_title_margin_bottom).toInt(),
|
||||
)
|
||||
.setTextColor(fgColor.toArgb())
|
||||
.setTextSize(16f)
|
||||
.build()
|
||||
)
|
||||
.setSubtitleStyle(
|
||||
TextViewStyle.Builder()
|
||||
.setLayoutMargin(
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_start).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_top).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_end).toInt(),
|
||||
context.resources.getDimension(R.dimen.suggestion_chip_fg_subtitle_margin_bottom).toInt(),
|
||||
)
|
||||
.setTextColor(ColorUtils.setAlphaComponent(fgColor.toArgb(), 150))
|
||||
.setTextSize(14f)
|
||||
.build()
|
||||
)
|
||||
.setEndIconStyle(
|
||||
ImageViewStyle.Builder()
|
||||
.setLayoutMargin(0, 0, 0, 0)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
stylesBuilder.addStyle(suggestionStyle)
|
||||
return stylesBuilder.build()
|
||||
setTextColor(ColorUtils.setAlphaComponent(fgColor.toArgb(), 150))
|
||||
setTextSize(14f)
|
||||
build()
|
||||
}
|
||||
val suggestionStyle = InlineSuggestionUi.newStyleBuilder().run {
|
||||
setSingleIconChipStyle(singleIconChipStyle)
|
||||
setChipStyle(chipStyle)
|
||||
setStartIconStyle(iconStyle)
|
||||
setEndIconStyle(iconStyle)
|
||||
setTitleStyle(titleStyle)
|
||||
setSubtitleStyle(subtitleStyle)
|
||||
build()
|
||||
}
|
||||
return UiVersions.newStylesBuilder().run {
|
||||
addStyle(suggestionStyle)
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getColorFromThemeAttribute(
|
||||
|
||||
@@ -361,10 +361,13 @@ class FlorisLocale private constructor(val base: Locale) {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun String.lowercase(locale: FlorisLocale): String = this.lowercase(locale.base)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun String.uppercase(locale: FlorisLocale): String = this.uppercase(locale.base)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun String.titlecase(locale: FlorisLocale = FlorisLocale.ROOT): String {
|
||||
return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale.base) else it.toString() }
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.structuralEqualityPolicy
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceObserver
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.provider.OpenableColumns
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.neverEqualPolicy
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.patrickgold.florisboard.app.ext.EditorAction
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.Backup
|
||||
@@ -188,7 +187,7 @@ class CacheManager(context: Context) {
|
||||
|
||||
var currentAction by mutableStateOf<EditorAction?>(null)
|
||||
var ext: Extension? = null
|
||||
var editor by mutableStateOf<T?>(null, neverEqualPolicy())
|
||||
var editor by mutableStateOf<T?>(null)
|
||||
var version by mutableIntStateOf(0)
|
||||
|
||||
val isModified get() = version > 0
|
||||
@@ -202,7 +201,6 @@ class CacheManager(context: Context) {
|
||||
inline fun <R> update(block: T.() -> R): R {
|
||||
// Method is designed to only be called when editor has been previously initialized
|
||||
val ret = block(editor!!)
|
||||
editor = editor
|
||||
version++
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.lib.compose
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
@@ -32,7 +32,7 @@ fun Modifier.rippleClickable(
|
||||
) = composed {
|
||||
this.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
indication = ripple(),
|
||||
enabled = enabled,
|
||||
onClickLabel = onClickLabel,
|
||||
role = role,
|
||||
|
||||
@@ -19,9 +19,9 @@ package dev.patrickgold.florisboard.lib.compose
|
||||
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
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
|
||||
@@ -25,9 +25,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -39,15 +37,10 @@ fun FlorisChip(
|
||||
onClick: () -> Unit = { },
|
||||
selected: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
color: Color = Color.Unspecified,
|
||||
shape: Shape = MaterialTheme.shapes.small,
|
||||
leadingIcons: List<ImageVector> = listOf(),
|
||||
trailingIcons: List<ImageVector> = listOf(),
|
||||
) {
|
||||
val backgroundColor = color.takeOrElse {
|
||||
MaterialTheme.colorScheme.onSurface.copy()
|
||||
}
|
||||
|
||||
InputChip(
|
||||
selected = selected,
|
||||
onClick = onClick,
|
||||
@@ -88,42 +81,4 @@ fun FlorisChip(
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/*Surface(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
color = backgroundColor,
|
||||
shape = shape,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp, horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
for (leadingIcon in leadingIcons) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.size(16.dp),
|
||||
imageVector = leadingIcon,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
for (trailingIcon in trailingIcons) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.size(16.dp),
|
||||
imageVector = trailingIcon,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
@@ -95,7 +95,7 @@ private class FlorisScreenScopeImpl : FlorisScreenScope {
|
||||
FlorisIconButton(
|
||||
onClick = { navController.popBackStack() },
|
||||
modifier = Modifier.autoMirrorForRtl(),
|
||||
icon = Icons.Default.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.lib.compose
|
||||
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridScope
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
fun LazyGridScope.header(
|
||||
key: Any? = null,
|
||||
content: @Composable LazyGridItemScope.() -> Unit,
|
||||
) {
|
||||
item(key, span = { GridItemSpan(this.maxLineSpan) }, content = content)
|
||||
}
|
||||
@@ -128,7 +128,7 @@ fun PreviewKeyboardField(
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { focusManager.clearFocus() },
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(autoCorrect = true),
|
||||
keyboardOptions = KeyboardOptions(autoCorrectEnabled = true),
|
||||
singleLine = true,
|
||||
shape = RectangleShape,
|
||||
colors = TextFieldDefaults.colors(
|
||||
|
||||
@@ -6,9 +6,9 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import org.florisboard.lib.android.AndroidSettingsHelper
|
||||
import org.florisboard.lib.android.SystemSettingsObserver
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import android.os.Debug
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import org.florisboard.lib.android.systemService
|
||||
import dev.patrickgold.florisboard.lib.titlecase
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils
|
||||
import dev.patrickgold.florisboard.lib.util.UnitUtils
|
||||
import org.florisboard.lib.android.systemService
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
@@ -49,6 +49,36 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDebugLogForGithub(context: Context, prefs: AppPrefs? = null, includeLogcat: Boolean = false): String {
|
||||
return buildString {
|
||||
appendLine("<details>")
|
||||
appendLine("<summary>Detailed info (Debug log header)</summary>")
|
||||
appendLine()
|
||||
appendLine("```")
|
||||
append(generateSystemInfoLog(context))
|
||||
appendLine()
|
||||
append(generateAppInfoLog(context))
|
||||
if (prefs != null) {
|
||||
appendLine()
|
||||
append(generateFeatureConfigLog(prefs))
|
||||
}
|
||||
appendLine()
|
||||
appendLine("```")
|
||||
appendLine("</details>")
|
||||
if (includeLogcat) {
|
||||
appendLine()
|
||||
appendLine("<details>")
|
||||
appendLine("<summary>Debug log content</summary>")
|
||||
appendLine()
|
||||
appendLine("```")
|
||||
append(generateLogcatDump())
|
||||
appendLine()
|
||||
appendLine("```")
|
||||
appendLine("</details>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSystemInfoLog(context: Context, withTitle: Boolean = true): String {
|
||||
return buildString {
|
||||
if (withTitle) appendLine("======= SYSTEM INFO =======")
|
||||
|
||||
@@ -21,9 +21,6 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.lib.ValidationRule
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.value.SnyggDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import dev.patrickgold.florisboard.lib.validate
|
||||
import org.florisboard.lib.snygg.value.SnyggVarValue
|
||||
|
||||
@@ -157,7 +154,7 @@ object ExtensionValidation {
|
||||
}
|
||||
|
||||
val SnyggSolidColorValue = ValidationRule<String> {
|
||||
forKlass = SnyggSolidColorValue::class
|
||||
forKlass = org.florisboard.lib.snygg.value.SnyggSolidColorValue::class
|
||||
forProperty = "color"
|
||||
validator { input ->
|
||||
val str = input.trim()
|
||||
@@ -172,7 +169,7 @@ object ExtensionValidation {
|
||||
}
|
||||
|
||||
val SnyggDpShapeValue = ValidationRule<String> {
|
||||
forKlass = SnyggDpShapeValue::class
|
||||
forKlass = org.florisboard.lib.snygg.value.SnyggDpShapeValue::class
|
||||
forProperty = "corner"
|
||||
validator { str ->
|
||||
val floatValue = str.toFloatOrNull()
|
||||
@@ -186,7 +183,7 @@ object ExtensionValidation {
|
||||
}
|
||||
|
||||
val SnyggPercentShapeValue = ValidationRule<String> {
|
||||
forKlass = SnyggPercentShapeValue::class
|
||||
forKlass = org.florisboard.lib.snygg.value.SnyggPercentShapeValue::class
|
||||
forProperty = "corner"
|
||||
validator { str ->
|
||||
val intValue = str.toIntOrNull()
|
||||
|
||||
@@ -13,9 +13,24 @@
|
||||
<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>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">سجل الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">تمكين سجل الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_history_enabled__summary" comment="Preference summary">الاحتفاظ بالرموز التعبيرية المستخدمة مؤخرًا للوصول السريع</string>
|
||||
<string name="prefs__media__emoji_history_pinned_update_strategy" comment="Preference title">تحديث الاستراتيجية (مثبت)</string>
|
||||
<string name="prefs__media__emoji_history_recent_update_strategy" comment="Preference title">إستراتيجية التحديث (الأخيرة)</string>
|
||||
<string name="prefs__media__emoji_history_max_size">الحد الأقصى للعناصر التي يجب الاحتفاظ بها</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">اقتراحات الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">تمكين اقتراحات الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">تقديم اقتراحات الرموز التعبيرية أثناء الكتابة</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">نوع التفعيل</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">تحديث سجل الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">قَبُول الرموز التعبيرية المقترحة يضيفها إلى تاريخ الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">عرض اسم الرمز التعبيري</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">تعرض اقتراحات الرموز التعبيرية اسمها جنبًا إلى جنب مع الرموز التعبيرية</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">الحد الأدنى لطول الاستعلام</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">الحد الأقصى لعدد المرشحين</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">وجوه تعبيرية و عواطف</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">أشخاص و أجسام</string>
|
||||
@@ -26,10 +41,12 @@
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">للوصول إلى سجل الرموز التعبيرية الخاص بك، يرجى أولاً إلغاء قُفْل جهازك.</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>
|
||||
<string name="emoji__history__empty_message" comment="Message if the emoji history is empty">لم يتم العثور على رموز تعبيرية مستخدمة مؤخرًا. بمجرد بَدْء كتابة الرموز التعبيرية، ستظهر تلقائيًا هنا.</string>
|
||||
<string name="emoji__history__phone_locked_message" comment="Message to show if phone is locked">للوصول إلى سجل الرموز التعبيرية الخاص بك، يرجى أولاً إلغاء قُفْل جهازك.</string>
|
||||
<string name="emoji__history__usage_tip" comment="Feature discoverability for actions of emoji history">نصيحة احترافية: اضغط لفترة طويلة على الرموز التعبيرية في سجل الرموز التعبيرية لتثبيتها أو إزالتها!</string>
|
||||
<string name="emoji__history__removal_success_message" comment="Toast message if user has used the delete action on an emoji in the emoji history">تمت إزالة {emoji} من سجل الرموز التعبيرية</string>
|
||||
<string name="emoji__history__pinned">مثبّت</string>
|
||||
<string name="emoji__history__recent">الحديثة</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">سهم للأعلى</string>
|
||||
<string name="quick_action__arrow_up__tooltip">تنفيذ السهم للأعلى</string>
|
||||
@@ -83,7 +100,7 @@
|
||||
<string name="incognito_mode__toast_after_disabled">الوضع الخاص هو الان معطل افتراضيا</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__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>
|
||||
@@ -124,6 +141,8 @@
|
||||
<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__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">تأكيد الحذف</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">هل أنت متيقِّن أنك تريد حذف النوع الفرعي؟</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>
|
||||
@@ -561,6 +580,8 @@
|
||||
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">إظهار حالة تراكب الإدخال لأغراض التطوير</string>
|
||||
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">أظهر طبقات التهجئة</string>
|
||||
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">إظهار نتائج تراكب التهجئة لأغراض التطوير</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__label">عرض تراكب الملء التلقائي المضمن</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__summary">تراكب مع نتائج الملء التلقائي المضمنة الحالية للتصحيح</string>
|
||||
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">عرض حدود لمس الازرار</string>
|
||||
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">رسم حد خارجي للمس الازرار باللون الاحمر</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">إظهار مساعد السحب والإفلات</string>
|
||||
@@ -576,6 +597,11 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">إعدادات أندرويد الأمنة</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">إعدادات نظام أندرويد</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">لغات النظام</string>
|
||||
<string name="devtools__debuglog__title">سجل التصحيح</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">تم نسخ سجل التصحيح إلى الحافظة</string>
|
||||
<string name="devtools__debuglog__copy_log">نسخ السجل</string>
|
||||
<string name="devtools__debuglog__copy_for_github">نسخ السجل (تنسيق GitHub)</string>
|
||||
<string name="devtools__debuglog__loading">تحميل…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">الإضافات والملحقات</string>
|
||||
<string name="ext__list__ext_theme">ملحقات السمات</string>
|
||||
@@ -734,6 +760,14 @@
|
||||
<string name="enum__display_language_names_in__system_locale__description" comment="Enum value description">يتم عرض أسماء اللغات عبر واجهة مستخدم التطبيق ولوحة المفاتيح باللغة المحلية التي تم تعيينها للجهاز بأكمله</string>
|
||||
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">اللغة الأصلية</string>
|
||||
<string name="enum__display_language_names_in__native_locale__description" comment="Enum value description">يتم عرض أسماء اللغات عبر واجهة مستخدم التطبيق ولوحة المفاتيح باللغة المحلية المشار إليها بنفسها</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend" comment="Enum value label">الفرز التلقائي (الإعداد المسبق)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend__description" comment="Enum value description">إعادة ترتيب الرموز التعبيرية تلقائيًا عند استخدام الرموز التعبيرية. تمت إضافة رموز تعبيرية جديدة إلى البداية.</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append" comment="Enum value label">الفرز التلقائي (إلحاق)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append__description" comment="Enum value description">إعادة ترتيب الرموز التعبيرية تلقائيًا عند استخدام الرموز التعبيرية. تتم إضافة رموز تعبيرية جديدة إلى النهاية.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend" comment="Enum value label">الفرز اليدوي (التمهيدي)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend__description" comment="Enum value description">لا تقم بإعادة جلب الرموز التعبيرية تلقائيًا عند استخدام الرموز التعبيرية. تمت إضافة رموز تعبيرية جديدة إلى البداية.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append" comment="Enum value label">الفرز اليدوي (إلحاق)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append__description" comment="Enum value description">لا تقم بإعادة ترتيب الرموز التعبيرية تلقائيًا عند استخدام الرموز التعبيرية. تتم إضافة رموز تعبيرية جديدة إلى النهاية.</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>
|
||||
@@ -745,6 +779,10 @@
|
||||
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} شعر مجعد</string>
|
||||
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} شعر ابيض</string>
|
||||
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} اصلع</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon">فاصلة متقدمة</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon__description" comment="Keep the :emoji_name while translating, this is a syntax guide">اقترح الرموز التعبيرية باستخدام بناء جملة:emoji_name</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text">نص المعلومات المضمنة</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text__description">اقترح الرموز التعبيرية ببساطة عن طريق كتابة اسم الرموز التعبيرية ككلمة</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates" comment="Enum value label">تجاهل المرشح</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates__description" comment="Enum value description">يضع صف الإجراءات الموسعة بين واجهة مستخدم التطبيق والصف المرشح</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">تحت المرشحون</string>
|
||||
@@ -876,4 +914,20 @@
|
||||
<item quantity="many">{v} عناصر</item>
|
||||
<item quantity="other">{v} عناصر</item>
|
||||
</plurals>
|
||||
<plurals name="unit__characters__written">
|
||||
<item quantity="zero">{v} حرف</item>
|
||||
<item quantity="one">{v} حرف</item>
|
||||
<item quantity="two">{v} حرفان</item>
|
||||
<item quantity="few">{v} أحرف</item>
|
||||
<item quantity="many">{v} احرف</item>
|
||||
<item quantity="other">{v} احرف</item>
|
||||
</plurals>
|
||||
<plurals name="unit__candidates__written">
|
||||
<item quantity="zero">{v} مرشح</item>
|
||||
<item quantity="one">{v} مرشح</item>
|
||||
<item quantity="two">{v} مرشحان</item>
|
||||
<item quantity="few">{v} مرشحات</item>
|
||||
<item quantity="many">{v} مرشحات</item>
|
||||
<item quantity="other">{v} مرشحون</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Fustaxes</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Fustaxes ASCII</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>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">Activar l\'historial de fustaxes</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Sorrises y fustaxes</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Persones y cuerpu</string>
|
||||
@@ -26,10 +26,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Oxetos</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Símbolos</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Banderes</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Nun s\'atopó nengún fustaxe que s\'usare apocayá. Namás que comiences a usalos, apaecen equí.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">P\'acceder al historial de fustaxes, desbloquia\'l preséu primero.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Conseyu: ¡ten primíos los fustaxes usaos apocayá pa volver quitalos d\'esta seición!</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">Quitóse «{emoji}» de los fustaxes usaos apocayá</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">سهم لأعلى</string>
|
||||
<string name="quick_action__arrow_up__tooltip">أداء السهم لأعلى</string>
|
||||
@@ -43,7 +39,7 @@
|
||||
<string name="quick_action__ime_ui_mode_clipboard" maxLength="12">Cartafueyu</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard__tooltip">Abrir l\'historial de fustaxes</string>
|
||||
<string name="quick_action__ime_ui_mode_media__tooltip">Abrir el panel de fustaxes</string>
|
||||
<string name="quick_action__settings" maxLength="12">Axustes</string>
|
||||
<string name="quick_action__settings" maxLength="12">Opciones</string>
|
||||
<string name="quick_action__settings__tooltip">Abrir la configuración</string>
|
||||
<string name="quick_action__undo" maxLength="12">Desfacer</string>
|
||||
<string name="quick_action__redo" maxLength="12">Refacer</string>
|
||||
@@ -54,12 +50,14 @@
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
<string name="settings__title" comment="Title of Settings">Configuración</string>
|
||||
<string name="settings__preview_keyboard" comment="Hint for try your setup box">Toca equí pa probar la configuración</string>
|
||||
<string name="settings__help" comment="General label for help buttons in Settings">Ayuda</string>
|
||||
<string name="settings__default" comment="General string which is used when a preference has the default value set">Por defeutu</string>
|
||||
<string name="settings__home__title" comment="Title of the Home screen">Afáyate en {app_name}</string>
|
||||
<string name="settings__localization__title" comment="Title of languages and Layout screen">Llingües y distribuciones</string>
|
||||
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Amosar los nomes de les llingües na</string>
|
||||
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">Sotipos</string>
|
||||
<string name="settings__localization__subtype_add_title" comment="Title of subtype dialog when adding a new subtype">Amestar un sotipu</string>
|
||||
<string name="settings__localization__language_pack_title" comment="Title of the language pack manager screen for managing installed and custom language packs">Xestionar los paquetes de llingua instalaos</string>
|
||||
<string name="settings__localization__language_pack_summary" comment="Summary of preference item for adding a new language pack">Opción esperimental: xestiona les estensiones qu\'amiesten compatibilidá pa llingües específiques (pel momentu, entrada china basada en formes)</string>
|
||||
<string name="settings__localization__subtype_locale" comment="Label for locale dropdown in subtype dialog">Llingua primaria</string>
|
||||
@@ -74,6 +72,12 @@
|
||||
<string name="settings__localization__subtype_phone_layout" comment="Label for layout dropdown in subtype dialog">Distribución primaria del tecláu telefónicu</string>
|
||||
<string name="settings__localization__subtype_phone2_layout" comment="Label for layout dropdown in subtype dialog">Distribución secundaria del tecláu telefónicu</string>
|
||||
<string name="settings__localization__subtype_summary" comment="Subtype summary">{characters_name} / {symbols_name} / {currency_set_name}</string>
|
||||
<string name="settings__localization__suggested_subtype_presets" comment="Suggested presets title">Sotipos preconfiguraos suxeríos</string>
|
||||
<string name="settings__localization__suggested_subtype_presets_none_found" comment="Suggested presets none found">Nun hai nengún stipu suxeríu. Usa\'l botón d\'abaxo pa ver tolos sotipos preconfiguraos.</string>
|
||||
<string name="settings__localization__subtype_presets" comment="Subtype presets dialog title">Sotipos preconfiguraos</string>
|
||||
<string name="settings__localization__subtype_presets_view_all" comment="View all presets button">Amosar too</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">Paez que nun configuresti nengún sotipu. ¡Va usase\'l sotipu «English/QWERTY» como alternativa!</string>
|
||||
<string name="settings__localization__subtype_error_already_exists" comment="Error message shown in subtype dialog when a subtype to add already exists">¡Esti sotipu yá esiste!</string>
|
||||
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Distribuciones</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Estilu</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Mou del estilu</string>
|
||||
@@ -153,6 +157,7 @@
|
||||
<string name="pref__gestures__space_bar_title" comment="Preference group title">Xestos de la barra d\'espaciu</string>
|
||||
<string name="settings__advanced__title" comment="Title of Advanced settings">Configuración avanzada</string>
|
||||
<string name="pref__advanced__settings_theme__label" comment="Label of Settings theme preference in Advanced">Estilu de la configuración</string>
|
||||
<string name="pref__advanced__settings_material_you__label" comment="Label of Material You preference in Advanced">Usar «Material You»</string>
|
||||
<string name="pref__advanced__settings_language__label" comment="Label of Settings language preference in Advanced">Llingua de la configuración</string>
|
||||
<string name="pref__advanced__show_app_icon__label" comment="Label of Show app icon preference in Advanced">Amosar l\'iconu l\'aplicación nel llanzador</string>
|
||||
<!-- About UI strings -->
|
||||
@@ -164,7 +169,7 @@
|
||||
<string name="about__version_copied__title" comment="Title of the toast for copying the version string">La versión copióse al cartafueyu</string>
|
||||
<string name="about__version_copied__error" comment="Title of the error toast for copying the version string">Prodúxose daqué malo: {error_message}</string>
|
||||
<string name="about__changelog__title" comment="Preference title">Rexistru de cambeos</string>
|
||||
<string name="about__changelog__summary" comment="Preference summary">Les novedaes</string>
|
||||
<string name="about__changelog__summary" comment="Preference summary">Contién les novedaes</string>
|
||||
<string name="about__repository__title" comment="Preference title">Depósitu (GitHub)</string>
|
||||
<string name="about__repository__summary" comment="Preference summary">El códigu fonte, los discutinios, los problemes y más información</string>
|
||||
<string name="about__privacy_policy__title" comment="Preference title">Política de privacidá</string>
|
||||
@@ -175,15 +180,25 @@
|
||||
<string name="about__third_party_licenses__summary" comment="Preference summary">Les llicencies de les biblioteques de terceros que s\'inclúin nesta aplicación</string>
|
||||
<!-- Setup UI strings -->
|
||||
<string name="setup__title" comment="Title of Setup">¡Afáyate!</string>
|
||||
<string name="setup__intro_message" comment="Short intro message welcoming new users">¡Gracies por usar {app_name}! Esta configuración rápida va guiate pelos pasos necesarios pa usar {app_name} nel preséu.</string>
|
||||
<string name="setup__footer__privacy_policy" comment="Privacy policy label for URL">Política de privacidá</string>
|
||||
<string name="setup__footer__repository" comment="Repository label for URL">Depósitu</string>
|
||||
<string name="setup__enable_ime__title">Activación de {app_name}</string>
|
||||
<string name="setup__enable_ime__description">Android rique que tolos teclaos personalizaos s\'activen per separtao enantes de poder usalos. Vete a la configuración de <i>Llingua y entrada</i> p\'activar «{app_name}» ellí.</string>
|
||||
<string name="setup__enable_ime__open_settings_btn">Abrir la configuración del sistema</string>
|
||||
<string name="setup__select_ime__title">Seleición de {app_name}</string>
|
||||
<string name="setup__select_ime__description">{app_name} yá ta activáu nel sistema. Pa usalu, escueyi «{app_name}» nel diálogu del selector d\'entrada.</string>
|
||||
<string name="setup__select_ime__switch_keyboard_btn">Cambiar de tecláu</string>
|
||||
<string name="setup__grant_notification_permission__title">Avisos pa informar de casques</string>
|
||||
<string name="setup__grant_notification_permission__description">A partir d\'Android 13, les aplicaciones tienen de pidir
|
||||
permisu pa unviar avisos. En FlorisBoard, esti permisu namás s\'usa p\'abrir la pantalla d\'informar casques al producise dalgún.
|
||||
Pues camudar esti permisu cuando quieras na configuración del sistema.
|
||||
</string>
|
||||
<string name="setup__grant_notification_permission__btn">Conceder el permisu</string>
|
||||
<string name="setup__finish_up__title">Fin</string>
|
||||
<string name="setup__finish_up__description_p1">{app_name} ta activáu nel sistema y yá pues personalizalu.</string>
|
||||
<string name="setup__finish_up__description_p2">Si atopes cualesquier problema, fallu, casque o namás quier facer dalguna suxerencia, revisa\'l depósitu del proyeutu na pantalla «Tocante a».</string>
|
||||
<string name="setup__finish_up__description_p2">Si atopes cualesquier problema, fallu, casque o namás quier facer dalguna suxerencia, consulta\'l depósitu del proyeutu na pantalla «Tocante a».</string>
|
||||
<string name="setup__finish_up__finish_btn">Comenzar a personalizar</string>
|
||||
<!-- Back up & Restore -->
|
||||
<string name="backup_and_restore__back_up__destination_file_sys">Sistema de ficheros llocal</string>
|
||||
<string name="backup_and_restore__back_up__destination_share_intent">Aplicación de terceros pel menú de compartición</string>
|
||||
@@ -213,7 +228,7 @@
|
||||
<string name="send_to_clipboard__type_not_supported_error">Esti elementu multimedia nun ye compatible.</string>
|
||||
<!-- Devtools strings -->
|
||||
<string name="devtools__title" comment="Title of Devtools screen. Translators: treat this string as 'Developer tools' for translation, except a similar short term is available for your language.">Ferramientes de desendolcu</string>
|
||||
<string name="devtools__enabled__summary" comment="Summary of Enable developer tools in Devtools">Ferramientes diseñaes específicamente pa depurar y iguar problemes</string>
|
||||
<string name="devtools__enabled__summary" comment="Summary of Enable developer tools in Devtools">Ferramientes diseñaes específicamente pa depurar ya iguar problemes</string>
|
||||
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Amosar les llendes táctiles de les tecles</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">Amosar los ayudantes de la función «drag&drop»</string>
|
||||
<string name="devtools__reset_flag_is_ime_set_up__summary" comment="Summary of Reset is IME set up flag in Devtools">Una aición de depuración pa volver amosar la pantalla de configuración</string>
|
||||
@@ -235,7 +250,7 @@
|
||||
<string name="ext__error__not_found_description">Nun se pudo atopar nenguna estensión cola ID «{id}».</string>
|
||||
<string name="ext__editor__title_create_any">Creación d\'una estensión</string>
|
||||
<string name="ext__editor__metadata__title_invalid">Los metadatos nun son válidos</string>
|
||||
<string name="ext__editor__metadata__message_invalid">Los metadatos d\'esta estensión nun son válidos. ¡Revisa l\'editor de metadatos pa consiguir más detalles!</string>
|
||||
<string name="ext__editor__metadata__message_invalid">Los metadatos d\'esta estensión nun son válidos. ¡Consulta l\'editor de metadatos pa consiguir más detalles!</string>
|
||||
<string name="ext__editor__dependencies__title">Xestionar les dependencies</string>
|
||||
<string name="ext__editor__create_component__title_theme">Creación d\'un estilu</string>
|
||||
<string name="ext__export__failure">La esportación de la estensión falló: {error_message}</string>
|
||||
@@ -246,6 +261,9 @@
|
||||
<string name="ext__validation__enter_title">Introduz un títulu</string>
|
||||
<string name="ext__validation__enter_valid_number">Introduz nun númberu válidu</string>
|
||||
<string name="ext__validation__enter_positive_number">Introduz un númberu positivu (>=0)</string>
|
||||
<string name="ext__update_box__search_for_updates">Buscar anovamientos</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Dir a «{ext_home_title}»</string>
|
||||
<string name="ext__home__visit_store">Visitar la tienda de complementos</string>
|
||||
<!-- Action strings -->
|
||||
<string name="action__add">Amestar</string>
|
||||
<string name="action__apply">Aplicar</string>
|
||||
|
||||
@@ -13,9 +13,24 @@
|
||||
<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>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">История на емоциите</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">Включване на историята на емоциите</string>
|
||||
<string name="prefs__media__emoji_history_enabled__summary" comment="Preference summary">Запазване на използваните емоции за по-бърз достъп</string>
|
||||
<string name="prefs__media__emoji_history_pinned_update_strategy" comment="Preference title">Стратегия за обновяване (закачени)</string>
|
||||
<string name="prefs__media__emoji_history_recent_update_strategy" comment="Preference title">Стратегия за обновяване (последни)</string>
|
||||
<string name="prefs__media__emoji_history_max_size">Максимален брой елементи</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">Предложения за емоции</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">Включване на предложенията за емоции</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">Показва предложения за емоции докато въвеждате</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">Вид на задействането</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">Обновяване на историята на емоциите</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">Приемане на предложената емиция я добавя в историята на емоции</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">Показване на името на емоцията</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">Предложенията за емоции включват и името</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">Минимална дължина на заявка</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">Максимален брой кандидати</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Усмивки и емоции</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Хора и тяло</string>
|
||||
@@ -26,10 +41,12 @@
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">За достъп до последно използваните емоции отключете устройството.</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>
|
||||
<string name="emoji__history__empty_message" comment="Message if the emoji history is empty">Скоро не сте използвали емоции. Започнете да използвате и ще бъдат показани тук.</string>
|
||||
<string name="emoji__history__phone_locked_message" comment="Message to show if phone is locked">За достъп до последно използваните емоции отключете устройството.</string>
|
||||
<string name="emoji__history__usage_tip" comment="Feature discoverability for actions of emoji history">Подсказка: Задръжте върху емоция в историята, за да я закачите или премахнате!</string>
|
||||
<string name="emoji__history__removal_success_message" comment="Toast message if user has used the delete action on an emoji in the emoji history">Емоцията {emoji} е премахната от историята</string>
|
||||
<string name="emoji__history__pinned">Закачени</string>
|
||||
<string name="emoji__history__recent">Последни</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Нагоре</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Извършва стрелка нагоре</string>
|
||||
@@ -89,13 +106,13 @@
|
||||
<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__title" comment="Title of the Home screen">Добре дошли във {app_name}</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__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__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__language_pack_title" comment="Title of the language pack manager screen for managing installed and custom language packs">Управление на инсталираните езикови пакети</string>
|
||||
<string name="settings__localization__language_pack_summary" comment="Summary of preference item for adding a new language pack">Ексоериментално: управление на добавки за поддръжка на ооределени езици (въвеждане чрез фигури на китайски език)</string>
|
||||
<string name="settings__localization__language_pack_summary" comment="Summary of preference item for adding a new language pack">Експериментално: управление на добавки за поддръжка на определени езици (въвеждане чрез фигури на китайски език)</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>
|
||||
@@ -124,6 +141,8 @@
|
||||
<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__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Потвърждение при премахване</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">Желаете ли да премахнете този подвид?</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>
|
||||
@@ -513,7 +532,7 @@
|
||||
<string name="clipboard__locked__message">За достъп до междинната памет отключете устройството.</string>
|
||||
<string name="clipboard__group_pinned">Закачени</string>
|
||||
<string name="clipboard__group_recent">Последни</string>
|
||||
<string name="clipboard__group_other">Друго</string>
|
||||
<string name="clipboard__group_other">Други</string>
|
||||
<string name="clipboard__item_description_email">Електронна поща</string>
|
||||
<string name="clipboard__item_description_url">Адрес</string>
|
||||
<string name="clipboard__item_description_phone">Телефон</string>
|
||||
@@ -540,7 +559,7 @@
|
||||
<string name="pref__clipboard__enable_clipboard_history__summary">Запазване на копираното за по-бърз достъп до него</string>
|
||||
<string name="pref__clipboard__clean_up_old__label">Премахване на стари елементи</string>
|
||||
<string name="pref__clipboard__clean_up_after__label">Премахване на старите елементи след</string>
|
||||
<string name="pref__clipboard__limit_history_size__label">Ограничаване на броя</string>
|
||||
<string name="pref__clipboard__limit_history_size__label">Ограничаване на броя елементи</string>
|
||||
<string name="pref__clipboard__max_history_size__label">Максимален брой елементи</string>
|
||||
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__label">Изчистване на текущата межд. памет влияе на историята</string>
|
||||
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__summary">Изчистване на текущото съдържание на междинната памет изчиства и последния елемент от историята</string>
|
||||
@@ -560,6 +579,8 @@
|
||||
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Слой със състоянието на полето за въвеждане, за отстраняване на дефекти</string>
|
||||
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Слой за проверка на правописа</string>
|
||||
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Слой със съдържанието от проверката на правопис, за отстраняване на дефекти</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__label">Автоматично попълване в слой</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__summary">Слой с резултатите от автоматичното попълване, за отстраняване на дефекти</string>
|
||||
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Видим контур на клавишите</string>
|
||||
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Оцветяване на контура на клавишите в червено</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">Манипулатори за влачене и пускане</string>
|
||||
@@ -575,6 +596,11 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Настройки на сигурността на Android</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Системни настройки на Android</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Езици на системата</string>
|
||||
<string name="devtools__debuglog__title">Дневник за отстраняване на дефекти</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">Съдържанието на дневника е копирано</string>
|
||||
<string name="devtools__debuglog__copy_log">Копиране</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Копиране (форматирано за Гитхъб)</string>
|
||||
<string name="devtools__debuglog__loading">Зареждане…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Добавки и разширения</string>
|
||||
<string name="ext__list__ext_theme">Добавки за теми</string>
|
||||
@@ -657,8 +683,8 @@
|
||||
<string name="ext__validation__enter_number_between_0_100">Въведете положително число между 0 и 100</string>
|
||||
<string name="ext__validation__hint_value_above_50_percent">Стойности над 50% ще бъдат приравнени на 50%, така че намалете стойността</string>
|
||||
<string name="ext__update_box__internet_permission_hint">Понеже приложението няма достъп до интернет, проверката за обновяване на инсталираните разширения се извършва ръчно.</string>
|
||||
<string name="ext__update_box__search_for_updates">Проверка за обновявания</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">Управлявани {extensions}</string>
|
||||
<string name="ext__update_box__search_for_updates">Проверка за обновяване</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">Управление на {extensions}</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">Всички дейности, свързани с внасяне, изнасяне, създаване, промяна и премахване на разширения могат да бъдат извършване през Управление на разширения.</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Към {ext_home_title}</string>
|
||||
<string name="ext__home__info">Можете да изтегляте и инсталирате разширения от магазина за добавки на FlorisBoard или да внесете файл на разширение, който сте изтеглили от интернет.</string>
|
||||
@@ -672,7 +698,7 @@
|
||||
<string name="action__back_up">Резервно копие</string>
|
||||
<string name="action__cancel">Отказ</string>
|
||||
<string name="action__create">Създаване</string>
|
||||
<string name="action__default">По подразбиране</string>
|
||||
<string name="action__default">Стандартни</string>
|
||||
<string name="action__delete">Премахване</string>
|
||||
<string name="action__delete_confirm_title">Потвърждаване на изтриване</string>
|
||||
<string name="action__delete_confirm_message">Наистина ли искате да изтриете „{name}“? Това действие веднъж изпълнено не може да бъде отменено.</string>
|
||||
@@ -733,6 +759,14 @@
|
||||
<string name="enum__display_language_names_in__system_locale__description" comment="Enum value description">Имената на езиците в приложението и клавиатурата са на подразбирания за устройствто език</string>
|
||||
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Присъщия за езика</string>
|
||||
<string name="enum__display_language_names_in__native_locale__description" comment="Enum value description">Имената на езиците в приложението и клавиатурата са на присъщия за езика</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend" comment="Enum value label">Автом. сортиране (добавяне отпред)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend__description" comment="Enum value description">Автоматично сортиране при използване на емоция. Новите емоции биват добавени в началото.</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append" comment="Enum value label">Автом. сортиране (добавяне отзад)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append__description" comment="Enum value description">Автоматично сортиране при използване на емоция. Новите емоции биват добавени в края.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend" comment="Enum value label">Ръчно сортиране (добавяне отпред)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend__description" comment="Enum value description">Без автоматично сортиране при използване на емоция. Новите емоции биват добавени в началото.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append" comment="Enum value label">Ръчно сортиране (добавяне отзад)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append__description" comment="Enum value description">Без автоматично сортиране при използване на емоция. Новите емоции биват добавени в края.</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>
|
||||
@@ -744,6 +778,10 @@
|
||||
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} Къдрава коса</string>
|
||||
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} Бял цвят на косата</string>
|
||||
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} Без коса</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon">Водещо двоеточие</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon__description" comment="Keep the :emoji_name while translating, this is a syntax guide">Предлагане на емоции чрез синтаксиса :име_на_емоция</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text">Обикновен текст</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text__description">Предлагане на емоции чрез въвеждане на името на емоцията като дума</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates" comment="Enum value label">Над кандидатите</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates__description" comment="Enum value description">Добавя се реда с допълнителни действия между приложението и реда с кандидати</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">Под кандидатите</string>
|
||||
@@ -859,4 +897,12 @@
|
||||
<item quantity="one">{v} елемент</item>
|
||||
<item quantity="other">{v} елемента</item>
|
||||
</plurals>
|
||||
<plurals name="unit__characters__written">
|
||||
<item quantity="one">{v} знак</item>
|
||||
<item quantity="other">{v} знака</item>
|
||||
</plurals>
|
||||
<plurals name="unit__candidates__written">
|
||||
<item quantity="one">{v} кандидат</item>
|
||||
<item quantity="other">{v} кандидата</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojis</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emoticones</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">Mida màxima de l\'historial d\'emojis</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">To de pell preferit dels emojis</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Pentinat preferit dels emojis</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objectes</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Símbols</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Banderes</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">No s\'han trobat emojis recents. En començar a escriure emojis, apareixeran automàticament aquí.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Consell: Manteniu premut els emojis recents per a suprimir-los d\'aquesta llista!</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">S\'ha suprimit {emoji} dels emojis recents</string>
|
||||
<!-- Quick action strings -->
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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 -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<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>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">چونە سەرەوە</string>
|
||||
<string name="quick_action__arrow_up__tooltip">چوونە سەرەتای دێڕ</string>
|
||||
|
||||
@@ -13,9 +13,24 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emotikony</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>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">Historie emotikonů</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">Zapnout historii emotikonů</string>
|
||||
<string name="prefs__media__emoji_history_enabled__summary" comment="Preference summary">Uchovávat nedávno použité emotikony pro rychlý přístup</string>
|
||||
<string name="prefs__media__emoji_history_pinned_update_strategy" comment="Preference title">Strategie aktualizace (připnuté)</string>
|
||||
<string name="prefs__media__emoji_history_recent_update_strategy" comment="Preference title">Strategie aktualizace (nedávné)</string>
|
||||
<string name="prefs__media__emoji_history_max_size">Maximální počet položek k uchování</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">Návrhy emotikonů</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">Povolit návrhy emotikonů</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">Poskytovat návrhy emotikonů, zatímco píšete</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">Typ spouštěče</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">Aktualizovat historii emotikonů</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">Přijetí navržených emotikonů je přidá do historie emotikonů</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">Zobrazit název emotikonu</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">Návrhy emotikonů zobrazují název vedle emotikonu</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">Minimální délka textu</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">Maximální počet kandidátů</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Smajlíky a emotikony</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Lidé a tělo</string>
|
||||
@@ -26,10 +41,12 @@
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">Pro přístup k historii smajlíků nejprve prosím odemkněte své zařízení.</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} bylo odebráno z nedávno použitých</string>
|
||||
<string name="emoji__history__empty_message" comment="Message if the emoji history is empty">Nenalezeny žádné nedávno použité emotikony. Jakmile je začnete psát, objeví se automaticky zde.</string>
|
||||
<string name="emoji__history__phone_locked_message" comment="Message to show if phone is locked">Pro přístup k historii emotikonů nejprve prosím odemkněte své zařízení.</string>
|
||||
<string name="emoji__history__usage_tip" comment="Feature discoverability for actions of emoji history">Tip: dlouze stiskněte emotikony v historii emotikonů pro jejich připnutí nebo odstranění!</string>
|
||||
<string name="emoji__history__removal_success_message" comment="Toast message if user has used the delete action on an emoji in the emoji history">Emotikon {emoji} odstraněn z historie</string>
|
||||
<string name="emoji__history__pinned">Připnuté</string>
|
||||
<string name="emoji__history__recent">Nedávné</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Šipka nahoru</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Vykonat šipku nahoru</string>
|
||||
@@ -124,6 +141,8 @@
|
||||
<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__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Potvrzení odstranění</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">Opravdu chcete odstranit tento podtyp?</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Motiv</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Režim motivu</string>
|
||||
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">Čas východu slunce</string>
|
||||
@@ -563,6 +582,8 @@
|
||||
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Zobrazí aktuální stav vstupu pro ladění</string>
|
||||
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Zobrazit překrytí s pravopisem</string>
|
||||
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Zobrazí aktuální výsledky pravopisu pro ladění</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__label">Zobrazit překrytí automatického vyplňování na řádku</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__summary">Zobrazí aktuální výsledky automatického vyplňování na řádku pro ladění</string>
|
||||
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Zobrazit hranice dotyku kláves</string>
|
||||
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Zobrazit červené ohraničení hranic dotyku kláves</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">Zobrazit pomocníky drag&drop</string>
|
||||
@@ -578,6 +599,11 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Bezpečná nastavení Androidu</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Systémová nastavení Androidu</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Systémové jazyky</string>
|
||||
<string name="devtools__debuglog__title">Protokol ladění</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">Protokol ladění zkopírován do schránky</string>
|
||||
<string name="devtools__debuglog__copy_log">Kopírovat protokol</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Kopírovat protokol (formátování GitHub)</string>
|
||||
<string name="devtools__debuglog__loading">Načítání…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Doplňky a rozšíření</string>
|
||||
<string name="ext__list__ext_theme">Rozšíření témat</string>
|
||||
@@ -663,7 +689,7 @@
|
||||
<string name="ext__update_box__search_for_updates">Vyhledat aktualizace</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">Správa {extensions}</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">Všechny úlohy spojené s importováním, exportováním, vytvářením, přizpůsobováním a odstraňováním rozšíření mohou být prováděny skrze centralizovaného správce doplňků.</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Přejít {ext_home_title}</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Přejít na {ext_home_title}</string>
|
||||
<string name="ext__home__info">Rozšíření můžete stahovat a instalovat z Obchodu s doplňky FlorisBoard nebo importovat jakýkoli soubor rozšíření, který jste si stáhli z internetu.</string>
|
||||
<string name="ext__home__visit_store">Navštívit Obchod s doplňky</string>
|
||||
<string name="ext__home__manage_extensions">Správa nainstalovaných doplňků</string>
|
||||
@@ -736,6 +762,14 @@
|
||||
<string name="enum__display_language_names_in__system_locale__description" comment="Enum value description">Názvy jazyků napříč aplikací a rozhraním klávesnice jsou zobrazeny v jazyce, který je nastaven pro celé zařízení</string>
|
||||
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Nativní jazyk</string>
|
||||
<string name="enum__display_language_names_in__native_locale__description" comment="Enum value description">Názvy jazyků napříč aplikací a rozhraním klávesnice jsou zobrazeny ve svém vlastním jazyce</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend" comment="Enum value label">Automatické řazení (na začátku)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend__description" comment="Enum value description">Automaticky seřazovat emotikony podle jejich používání. Nové emotikony budou přidávány na začátek.</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append" comment="Enum value label">Automatické řazení (na konci)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append__description" comment="Enum value description">Automaticky seřazovat emotikony podle jejich používání. Nové emotikony budou přidávány na konec.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend" comment="Enum value label">Ruční řazení (na začátku)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend__description" comment="Enum value description">Neseřazovat automaticky emotikony podle jejich používání. Nové emotikony budou přidávány na začátek.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append" comment="Enum value label">Ruční řazení (na konci)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append__description" comment="Enum value description">Neseřazovat automaticky emotikony podle jejich používání. Nové emotikony budou přidávány na konec.</string>
|
||||
<string name="enum__emoji_skin_tone__default" comment="Enum value label">{emoji} Výchozí barva pleti</string>
|
||||
<string name="enum__emoji_skin_tone__light_skin_tone" comment="Enum value label">{emoji} Světlá barva pleti</string>
|
||||
<string name="enum__emoji_skin_tone__medium_light_skin_tone" comment="Enum value label">{emoji} Středně světlá barva pleti</string>
|
||||
@@ -747,9 +781,13 @@
|
||||
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} Kurdnaté vlasy</string>
|
||||
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} Bílé vlasy</string>
|
||||
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} Pleš</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon">Počáteční dvojtečka</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon__description" comment="Keep the :emoji_name while translating, this is a syntax guide">Navrhovat emotikony pomocí syntaxe :emoji_name</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text">Text v řádku</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text__description">Navrhovat emotikony jednoduše zadáním názvu emotikonu jako slova</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates" comment="Enum value label">Nad navrženými slovy</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates__description" comment="Enum value description">Umístí řádek rozšířených akcí mezi UI aplikace a řádek navrhnutých slov</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">Pod navrhnutými slovy</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">Pod navrženými slovy</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates__description" comment="Enum value description">Umístí řádek rozšířených akcí mezi řádek s navrhnutými slovy a textovou klávesnici</string>
|
||||
<string name="enum__extended_actions_placement__overlay_app_ui" comment="Enum value label">Překrýt rozhraní aplikace</string>
|
||||
<string name="enum__extended_actions_placement__overlay_app_ui__description" comment="Enum value description">Umístí řádek rozšířených akcí jako překrytí nad UI aplikace, bez ovlivnění výšky rozhraní klávesnice. Vezměte prosím na vědomí, že toto umístění může způsobit částečné překreslení vstupního pole aplikace</string>
|
||||
@@ -870,4 +908,16 @@
|
||||
<item quantity="many">{v} položek</item>
|
||||
<item quantity="other">{v} položek</item>
|
||||
</plurals>
|
||||
<plurals name="unit__characters__written">
|
||||
<item quantity="one">{v} znak</item>
|
||||
<item quantity="few">{v} znaky</item>
|
||||
<item quantity="many">{v} znaků</item>
|
||||
<item quantity="other">{v} znaků</item>
|
||||
</plurals>
|
||||
<plurals name="unit__candidates__written">
|
||||
<item quantity="one">{v} návrh</item>
|
||||
<item quantity="few">{v} návrhy</item>
|
||||
<item quantity="many">{v} návrhů</item>
|
||||
<item quantity="other">{v} návrhů</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojis</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Humørikoner</string>
|
||||
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Tekst-emoji</string>
|
||||
<string name="prefs__media__emoji_recently_used_max_size">Maks. størrelse på emoji-historik</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Foretrukken emoji-hudfarve</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Foretrukken emoji-frisure</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,10 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objekter</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Symboler</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Flag</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Ingen nyligt brugte emojis fundet. Når du begynder at skrive emojis, vil de automatisk blive vist her.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">For at få adgang til din emoji-historik skal du først låse din enhed op.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro tip: Tryk længe på nyligt brugte emojis for at fjerne dem fra denne visning igen!</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">Fjernet {emoji} fra nyligt brugte emojis</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Pil op</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Udfør pil op</string>
|
||||
@@ -52,10 +47,13 @@
|
||||
<string name="quick_action__toggle_incognito_mode__tooltip">Slå inkognito-tilstand til/fra</string>
|
||||
<string name="quick_action__voice_input" maxLength="12">Stemmeinput</string>
|
||||
<string name="quick_action__voice_input__tooltip" comment="IME stands for Input Method Editor and is indirectly equivalent to 'keyboard'.">Åbn stemmeinput-udbyder</string>
|
||||
<string name="quick_action__one_handed_mode__tooltip">Skift enhåndstilstand</string>
|
||||
<string name="quick_action__drag_marker__tooltip" comment="This action is only used as a placeholder in the actions editor drag and drop screen and only visible in debug mode">Nuværende træk-markør position</string>
|
||||
<string name="quick_action__noop" maxLength="12" comment="Noop=no operation; this action is only used as a placeholder in the actions editor drag and drop screen">Ingen</string>
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
<string name="settings__title" comment="Title of Settings">Indstillinger</string>
|
||||
<string name="settings__preview_keyboard" comment="Hint for try your setup box">Prøv din opsætning</string>
|
||||
<string name="settings__help" comment="General label for help buttons in Settings">Hjælp</string>
|
||||
<string name="settings__default" comment="General string which is used when a preference has the default value set">Standard</string>
|
||||
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">Systemstandard</string>
|
||||
@@ -69,11 +67,14 @@
|
||||
<string name="settings__localization__language_pack_title" comment="Title of the language pack manager screen for managing installed and custom language packs">Administrér installerede sprogpakker</string>
|
||||
<string name="settings__localization__subtype_edit_title" comment="Title of subtype dialog when editing an existing subtype">Ændre undertastatur</string>
|
||||
<string name="settings__localization__subtype_locale" comment="Label for locale dropdown in subtype dialog">Primært sprog</string>
|
||||
<string name="settings__localization__subtype_suggestion_provider" comment="Label for suggestion provider dropdown in subtype dialog">Inkognito-tilstand er nu aktiveret. Vil ikke lærer ord fra dine input mens denne tilstand er aktiv</string>
|
||||
<string name="settings__localization__subtype_select_locale" comment="Subtype select language title">Vælg sprog</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">Det ser ud til, at der ikke er konfigureret nogle undertastaturer. I dette tilfælde faldes der tilbage på Engelsk/QWERTY!</string>
|
||||
<string name="settings__localization__subtype_error_already_exists" comment="Error message shown in subtype dialog when a subtype to add already exists">Dette undertastatur findes allerede!</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} (ikke installeret)</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Tema</string>
|
||||
<string name="settings__theme_editor__rule_selectors"></string>
|
||||
<string name="snygg__rule_element__keyboard">Tastatur vindue</string>
|
||||
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">Aktivér lydfeedback</string>
|
||||
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">Vibrationsvarighed</string>
|
||||
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">Vibrationsstyrke</string>
|
||||
|
||||
@@ -13,9 +13,24 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojis</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">Maximale Größe des Emoji-Verlaufs</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Bevorzugte Emoji-Hautfarbe</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Bevorzugte Emoji-Haarfarbe</string>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">Emoji-Historie</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">Aktiviere Emoji-Historie</string>
|
||||
<string name="prefs__media__emoji_history_enabled__summary" comment="Preference summary">Behalte kürzlich genutzte Emojis für einen schnellen Zugriff</string>
|
||||
<string name="prefs__media__emoji_history_pinned_update_strategy" comment="Preference title">Update Strategie (gepinnt)</string>
|
||||
<string name="prefs__media__emoji_history_recent_update_strategy" comment="Preference title">Update Strategie (kürzlich)</string>
|
||||
<string name="prefs__media__emoji_history_max_size">Maximale zu behaltende Emojis</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">Emoji Vorschläge</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">Aktiviere Emoji Vorschläge</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">Erhalte Emoji Vorschläge während des Tippens</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">Auslösetyp</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">Update Emoji-Historie</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">Angenommene vorgeschlagene Emojis werden zur Emoji-Historie hinzugefügt</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">Zeige Emoji Name</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">Emoji Vorschläge zeigen den Namen neben dem Emoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">Minimale Suchbegriff-Länge</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">Maximale Anzahl von Vorschlägen</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Smileys & Emotionen</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Personen & Körper</string>
|
||||
@@ -26,10 +41,12 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objekte</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Symbole</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Flaggen</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Keine kürzlich verwendeten Emojis gefunden. Sobald Sie Emojis verwendet haben erscheinen diese hier.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">Um auf deinen Emoji-verlauf zuzugreifen, entsperre zuerst dein Gerät.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro Tipp: Halten Sie kürzlich verwendete Emojis gedrückt, um diese wieder aus dieser Ansicht zu entfernen!</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} wurde aus \"kürzlich verwendeten Emojis\" entfernt</string>
|
||||
<string name="emoji__history__empty_message" comment="Message if the emoji history is empty">Keine kürzlich benutzen Emojis gefunden. Sobald Emojis benutzt werden, erscheinen diese hier.</string>
|
||||
<string name="emoji__history__phone_locked_message" comment="Message to show if phone is locked">Um auf deinen Emoji-Historie zuzugreifen, entsperre das Gerät.</string>
|
||||
<string name="emoji__history__usage_tip" comment="Feature discoverability for actions of emoji history">Tipp: Tippe lange auf Emojis in der Historie um sie anzupinnen oder zu entfernen!</string>
|
||||
<string name="emoji__history__removal_success_message" comment="Toast message if user has used the delete action on an emoji in the emoji history">{emoji} aus Historie gelöscht</string>
|
||||
<string name="emoji__history__pinned">Angepinnt</string>
|
||||
<string name="emoji__history__recent">Kürzlich</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Pfeil hoch</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Pfeil nach oben ausführen</string>
|
||||
@@ -124,6 +141,8 @@
|
||||
<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)">In mindestens einem Feld ist kein Wert ausgewählt. Bitte wähle einen Wert für das Feld / die Felder.</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} (nicht installiert)</string>
|
||||
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Layouts</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Löschen bestätigen</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">Bist du sicher, dass du diesen Untertyp löschen willst?</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Design</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Design-Modus</string>
|
||||
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">Sonnenuntergangszeit</string>
|
||||
@@ -160,7 +179,7 @@
|
||||
<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_started">Key code aufnahme gestartet</string>
|
||||
<string name="settings__theme_editor__code_recording_stopped">Key code aufnahme beendet</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_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>
|
||||
@@ -378,7 +397,7 @@
|
||||
<string name="settings__udm__dialog__shortcut_label" comment="Label for the shortcut in the user dictionary add/edit dialog">Abkürzung (optional)</string>
|
||||
<string name="settings__udm__dialog__shortcut_error_invalid" comment="Error label for the shortcut in the user dictionary add/edit dialog">Bitte einen Shortcut eingeben, der zu {regex} passt</string>
|
||||
<string name="settings__udm__dialog__locale_label" comment="Label for the language code in the user dictionary add/edit dialog">Sprachcode (optional)</string>
|
||||
<string name="settings__udm__dialog__locale_error_invalid" comment="Error label for the language code in the user dictionary add/edit dialog">Dieses Sprachcode entspicht nicht denn enstpächende Syntax. Der Code muss entsprechend von Land (wie en), Land und Region (wie en_US) oder Land, Region und Script (wie en_US-script).</string>
|
||||
<string name="settings__udm__dialog__locale_error_invalid" comment="Error label for the language code in the user dictionary add/edit dialog">Dieser Sprachcode entspricht nicht der Syntax. Der Code muss entweder ein Land (wie en), Land und Region (wie en_US) oder Land, Region und Script (wie en_US-script) sein.</string>
|
||||
<string name="settings__gestures__title" comment="Title of Gestures screen">Gesten & Glide Typing</string>
|
||||
<string name="pref__glide__title" comment="Preference group title">Glide Typing</string>
|
||||
<string name="pref__glide__enabled__label" comment="Preference title">Glide Typing aktivieren</string>
|
||||
@@ -433,7 +452,7 @@
|
||||
<string name="about__privacy_policy__title" comment="Preference title">Datenschutzrichtlinie</string>
|
||||
<string name="about__privacy_policy__summary" comment="Preference summary">Datenschutzrichtlinie für dieses Projekt</string>
|
||||
<string name="about__project_license__title" comment="Preference title">Projekt-Lizenz</string>
|
||||
<string name="about__project_license__summary" comment="Preference summary">FlorisBoard ist unter {license_name} lizensiert</string>
|
||||
<string name="about__project_license__summary" comment="Preference summary">FlorisBoard ist unter {license_name} lizenziert</string>
|
||||
<string name="about__project_license__error_license_text_failed" comment="Error text for license text loading failure">Fehler: Lizenztext konnte nicht geladen werden.\n-> Grund: {error_message}</string>
|
||||
<string name="about__project_license__error_reason_asset_manager_null" comment="Error text if asset manager is null">Assetmanager Referenz ist null</string>
|
||||
<string name="about__third_party_licenses__title" comment="Preference title">Drittanbieter-Lizenzen</string>
|
||||
@@ -574,7 +593,16 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Sichere Android-Einstellungen</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Android System-Einstellungen</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Systemgebietsschemata</string>
|
||||
<string name="devtools__debuglog__title">Debug Log</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">Debug Log wurde in die Zwischenablage kopiert</string>
|
||||
<string name="devtools__debuglog__copy_log">Log kopieren</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Log kopieren (GitHub Formatierung)</string>
|
||||
<string name="devtools__debuglog__loading">Lädt…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Add-ons & Erweiterungen</string>
|
||||
<string name="ext__list__ext_theme">Design-Erweiterungen</string>
|
||||
<string name="ext__list__ext_keyboard">Tastatur-Erweiterungen</string>
|
||||
<string name="ext__list__ext_languagepack">Sprachpaket-Erweiterungen</string>
|
||||
<string name="ext__meta__authors">Ersteller</string>
|
||||
<string name="ext__meta__components">Gebündelte Komponenten</string>
|
||||
<string name="ext__meta__components_theme">Enthaltene Designs</string>
|
||||
@@ -627,6 +655,40 @@
|
||||
<string name="ext__import__file_skip_ext_not_supported" comment="Reason string when file is loaded in incorrect context">Eine Mediendatei (Bild, Audio, Text, etc.) wurde erwartet, aber ein Erweiterungsarchiv wurde gefunden.</string>
|
||||
<string name="ext__import__file_skip_media_not_supported" comment="Reason string when file is loaded in incorrect context">Ein Erweiterungsarchiv wurde erwartet, aber eine Mediendatei (Bild, Audio, Text, etc.) wurde gefunden.</string>
|
||||
<string name="ext__import__error_unexpected_exception" comment="Label when an error occurred during import. The error message will be appended below this text view">Beim Import ist ein unerwarteter Fehler aufgetreten. Folgende Angaben wurden gemacht:</string>
|
||||
<string name="ext__validation__enter_package_name">Bitte gib einen Paketnamen ein</string>
|
||||
<string name="ext__validation__error_package_name">Paketname stimmt nicht mit Regex {id_regex} überein</string>
|
||||
<string name="ext__validation__enter_version">Bitte gib eine Version an</string>
|
||||
<string name="ext__validation__enter_title">Bitte gib einen Titel ein</string>
|
||||
<string name="ext__validation__enter_maintainer">Bitte gib mindestens einen gültigen Maintainer an</string>
|
||||
<string name="ext__validation__enter_license">Bitte gib eine Lizenzbezeichnung an</string>
|
||||
<string name="ext__validation__enter_component_id">Bitte gib eine Komponenten-ID ein</string>
|
||||
<string name="ext__validation__error_component_id">Bitte eine Komponenten-ID eingeben, welche zu {component_id_regex} passt</string>
|
||||
<string name="ext__validation__enter_component_label">Bitte gib eine Komponentenbezeichnung ein</string>
|
||||
<string name="ext__validation__hint_component_label_to_long">Die Komponentenbezeichnung ist recht lang, was zu einem Abschneiden auf der Benutzeroberfläche führen kann</string>
|
||||
<string name="ext__validation__error_author">Bitte gib mindestens einen gültigen Autor an</string>
|
||||
<string name="ext__validation__error_stylesheet_path_blank">Der Stylesheet-Pfad darf nicht leer sein</string>
|
||||
<string name="ext__validation__error_stylesheet_path">Bitte einen Stylesheet-Pfad eingeben, welcher zu {stylesheet_path_regex} passt</string>
|
||||
<string name="ext__validation__enter_property">Bitte gib einen Variablennamen an</string>
|
||||
<string name="ext__validation__error_property">Bitte einen gültigen Variablennamen eingeben, welcher zu {variable_name_regex} passt</string>
|
||||
<string name="ext__validation__hint_property" tools:ignore="TypographyDashes">Nach FlorisCSS-Konvention beginnt ein Variablenname mit zwei Bindestrichen (--)</string>
|
||||
<string name="ext__validation__enter_color">Bitte gib einen Farbstring ein</string>
|
||||
<string name="ext__validation__error_color">Bitte gib einen gültigen Farbstring ein</string>
|
||||
<string name="ext__validation__enter_dp_size">Bitte gib eine dp-Größe an</string>
|
||||
<string name="ext__validation__enter_valid_number">Bitte gib eine gültige Nummer ein</string>
|
||||
<string name="ext__validation__enter_positive_number">Bitte gib eine positive Zahl ein (>=0)</string>
|
||||
<string name="ext__validation__enter_percent_size">Bitte gib eine Prozentzahl an</string>
|
||||
<string name="ext__validation__enter_number_between_0_100">Bitte gib eine positive Zahl zwischen 0 und 100 an</string>
|
||||
<string name="ext__validation__hint_value_above_50_percent">Jeder Wert über 50 % verhält sich so, als ob 50 % eingestellt wären. Überlege den Wert zu senken</string>
|
||||
<string name="ext__update_box__internet_permission_hint">Weil diese App keine Internetberechtigungen hat, muss manuell nach Updates für Erweiterungen gesucht werden.</string>
|
||||
<string name="ext__update_box__search_for_updates">Nach Updates suchen</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">Verwalte {extensions}</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">Alle Aufgaben im Zusammenhang mit dem Importieren, Exportieren, Erstellen, Anpassen und Entfernen von Erweiterungen können über den Add-on-Manager abgewickelt werden.</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Gehe zu {ext_home_title}</string>
|
||||
<string name="ext__home__info">Erweiterungen können über den FlorisBoard Add-on Store heruntergeladen und installiert werden oder importiere beliebige Erweiterungen aus dem Internet.</string>
|
||||
<string name="ext__home__visit_store">Besuche den Add-ons Store</string>
|
||||
<string name="ext__home__manage_extensions">Installierte Erweiterungen verwalten</string>
|
||||
<string name="ext__list__view_details">Details anzeigen</string>
|
||||
<string name="ext__check_updates__title">Auf Updates prüfen</string>
|
||||
<!-- Action strings -->
|
||||
<string name="action__add">Hinzufügen</string>
|
||||
<string name="action__apply">Übernehmen</string>
|
||||
@@ -694,6 +756,14 @@
|
||||
<string name="enum__display_language_names_in__system_locale__description" comment="Enum value description">Sprachnamen in der App und in der Tastatur werden in der Systemsprache des Geräts dargestellt</string>
|
||||
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Systemsprache</string>
|
||||
<string name="enum__display_language_names_in__native_locale__description" comment="Enum value description">Sprachnamen in der App und in der Tastatur werden in der referenzierten Sprache dargestellt</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend" comment="Enum value label">Automatisch Sortieren (Voranstellen)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend__description" comment="Enum value description">Emojis werden bei Verwendung neu sortiert. Neue Emojis werden am Anfang hinzugefügt.</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append" comment="Enum value label">Automatisch Sortieren (Anhängen)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append__description" comment="Enum value description">Emojis werden bei Verwendung neu sortiert. Neue Emojis werden am Ende hinzugefügt.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend" comment="Enum value label">Manuelles Sortieren (Voranstellen)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend__description" comment="Enum value description">Emojis werden bei Verwendung nicht neu sortiert. Neue Emojis werden am Anfang hinzugefügt.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append" comment="Enum value label">Manuelles Sortieren (Anhängen)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append__description" comment="Enum value description">Emojis werden bei Verwendung nicht neu sortiert. Neue Emojis werden am Ende hinzugefügt.</string>
|
||||
<string name="enum__emoji_skin_tone__default" comment="Enum value label">{emoji} Standard Hautfarbe</string>
|
||||
<string name="enum__emoji_skin_tone__light_skin_tone" comment="Enum value label">{emoji} Helle Hautfarbe</string>
|
||||
<string name="enum__emoji_skin_tone__medium_light_skin_tone" comment="Enum value label">{emoji} Mittel helle Hautfarbe</string>
|
||||
@@ -705,6 +775,10 @@
|
||||
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} Lockige Haare</string>
|
||||
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} Weiße Haare</string>
|
||||
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} Glatze</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon">Führender Doppelpunkt</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon__description" comment="Keep the :emoji_name while translating, this is a syntax guide">Emojis mit der Syntax :emoji_name vorschlagen</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text">Eingebetteter Text</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text__description">Schlage Emojis vor, indem einfach der Emoji-Namen als Wort eingeben wird</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates" comment="Enum value label">Über Kandidaten</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates__description" comment="Enum value description">Platziert die erweiterte Aktionsleiste zwischen der App-Oberfläche und der Kandidatenleiste</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">Unter Kandidaten</string>
|
||||
@@ -820,4 +894,12 @@
|
||||
<item quantity="one">{v} Element</item>
|
||||
<item quantity="other">{v} Elemente</item>
|
||||
</plurals>
|
||||
<plurals name="unit__characters__written">
|
||||
<item quantity="one">{v} Zeichen</item>
|
||||
<item quantity="other">{v} Zeichen</item>
|
||||
</plurals>
|
||||
<plurals name="unit__candidates__written">
|
||||
<item quantity="one">{v} Vorschlag</item>
|
||||
<item quantity="other">{v} Vorschläge</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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">Εικονίδια emoticon</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">Μέγιστο μέγεθος ιστορικού εικονιδίων emoji</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Προτιμώμενος τόνος επιδερμίδας εικονιδίων emoji</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Φατσούλες & Συναισθήματα</string>
|
||||
|
||||
@@ -13,9 +13,18 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojis</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emoticonos</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">Tamaño máximo de historial de emojis</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Tono de piel de emoji preferido</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Estilo de cabello de emoji preferido</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">Sugerencias de emoticonos</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">Habilitar sugerencias de emoticonos</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">Proporcionar sugerencias de emoticonos mientras escribes</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">Tipo de acción</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">Actualizar el historial de emoticonos</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">Al aceptar emoticonos sugeridos, se añaden al historial de emoticonos</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">Muestra el nombre de emoticono</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">Las sugerencias de emoticono muestran su nombre junto al emoticono</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">Longitud mínima de consulta</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">Número máximo de candidatos</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Smileys & Emociones</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Personas & Cuerpo</string>
|
||||
@@ -26,20 +35,25 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objetos</string>
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">Para acceder a tu historial de emojis, por favor, desbloquea primero tu dispositivo.</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>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Arriba</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Realizar flecha hacia arriba</string>
|
||||
<string name="quick_action__arrow_down" maxLength="12">Abajo</string>
|
||||
<string name="quick_action__arrow_down__tooltip">Realizar flecha hacia abajo</string>
|
||||
<string name="quick_action__arrow_left" maxLength="12">Izquierda</string>
|
||||
<string name="quick_action__arrow_left__tooltip">Realizar flecha hacia la izquierda</string>
|
||||
<string name="quick_action__arrow_right" maxLength="12">Derecha</string>
|
||||
<string name="quick_action__arrow_right__tooltip">Realizar flecha hacia la derecha</string>
|
||||
<string name="quick_action__clipboard_clear_primary_clip" maxLength="12">Borrar clip</string>
|
||||
<string name="quick_action__clipboard_clear_primary_clip__tooltip">Realizar limpiar portapapeles principal</string>
|
||||
<string name="quick_action__clipboard_copy" maxLength="12">Copiar</string>
|
||||
<string name="quick_action__clipboard_copy__tooltip">Realizar copiar portapapeles</string>
|
||||
<string name="quick_action__clipboard_cut" maxLength="12">Cortar</string>
|
||||
<string name="quick_action__clipboard_cut__tooltip">Realizar cortar portapapeles</string>
|
||||
<string name="quick_action__clipboard_paste" maxLength="12">Pegar</string>
|
||||
<string name="quick_action__clipboard_paste__tooltip">Realizar pegar portapapeles</string>
|
||||
<string name="quick_action__clipboard_select_all" maxLength="12">Seleccionar</string>
|
||||
<string name="quick_action__clipboard_select_all__tooltip">Realizar seleccionar todo en portapapeles</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard" maxLength="12">Portapapeles</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard__tooltip">Abrir historial del portapapeles</string>
|
||||
<string name="quick_action__ime_ui_mode_media" maxLength="12">Emoji</string>
|
||||
@@ -115,6 +129,8 @@
|
||||
<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)">Al menos un campo no posee un valor seleccionado. Por favor escoja un valor para el(los) campo(s).</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} (no instalado)</string>
|
||||
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Distribuciones</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Confirmación de eliminación</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">¿Está seguro de que desea eliminar este subtipo?</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Tema</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Tema</string>
|
||||
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">Amanecer</string>
|
||||
@@ -138,6 +154,7 @@
|
||||
<string name="settings__theme_editor__rule_element">Elemento destino</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_shift_states">Cambiar estados</string>
|
||||
<string name="settings__theme_editor__rule_selectors">Selectores</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>
|
||||
@@ -176,11 +193,23 @@
|
||||
<string name="snygg__rule_element__emoji_key_tab">Pestaña de emojis</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_layout">Disposición de la entrada del paisaje</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_field">Campo de entrada en paisaje</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_action">Acción de entrada apaisada</string>
|
||||
<string name="snygg__rule_element__glide_trail">Rastro del gesto</string>
|
||||
<string name="snygg__rule_element__incognito_mode_indicator">Indicador de modo incógnito</string>
|
||||
<string name="snygg__rule_element__one_handed_panel">Panel a una mano</string>
|
||||
<string name="snygg__rule_element__smartbar">Barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_row">Fila de acciones compartidas de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_toggle">Alternar acciones compartidas de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_row">Fila de acciones extendidas de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_toggle">Alternar acciones extendidas de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_action_key">Tecla de acción de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_action_tile">Mosaico de acción de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_overflow">Acciones de overflow de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_overflow_customize_button">Botón de personalización de overflow de acciones de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_editor">Editor de acciones de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_editor_header">Cabecera del editor de acciones de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_editor_subheader">Subencabezado del editor de acciones de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_candidates_row">Fila de candidatos de la barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_candidate_word">Palabra candidata de barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_candidate_clip">Clip candidato de barra inteligente</string>
|
||||
<string name="snygg__rule_element__smartbar_candidate_spacer">Espaciado candidato de barra inteligente</string>
|
||||
@@ -234,6 +263,7 @@
|
||||
<string name="settings__input_feedback__title" comment="Title of Input Feedback screen">Sonidos & Vibración</string>
|
||||
<string name="pref__input_feedback__group_audio__label" comment="Preference group title">Respuesta de audio / Sonidos</string>
|
||||
<string name="pref__input_feedback__audio_enabled__label" comment="Preference title">Habilitar respuesta de audio</string>
|
||||
<string name="pref__input_feedback__audio_enabled__summary_disabled" comment="Preference summary">No reproducir nunca sonidos para eventos de entrada, independientemente de la configuración del sistema</string>
|
||||
<string name="pref__input_feedback__audio_volume__label" comment="Preference title">Volumen de sonido para eventos de entrada</string>
|
||||
<string name="pref__input_feedback__audio_feat_key_press__label" comment="Preference title">Sonidos al presionar una tecla</string>
|
||||
<string name="pref__input_feedback__audio_feat_key_long_press__label" comment="Preference title">Sonidos al presionar prolongadamente</string>
|
||||
@@ -242,6 +272,7 @@
|
||||
<string name="pref__input_feedback__audio_feat_gesture_moving_swipe__label" comment="Preference title">Sonidos de gestos deslizantes con movimiento</string>
|
||||
<string name="pref__input_feedback__group_haptic__label" comment="Preference group title">Respuesta táctil / Vibración</string>
|
||||
<string name="pref__input_feedback__haptic_enabled__label" comment="Preference title">Habilitar respuesta táctil</string>
|
||||
<string name="pref__input_feedback__haptic_enabled__summary_disabled" comment="Preference summary">Nunca vibrar por eventos de entrada, independientemente de la configuración del sistema</string>
|
||||
<string name="pref__input_feedback__haptic_vibration_mode__label" comment="Preference title">Modo de vibración</string>
|
||||
<string name="pref__input_feedback__haptic_vibration_duration__label" comment="Preference title">Duración de vibración</string>
|
||||
<string name="pref__input_feedback__haptic_vibration_strength__label" comment="Preference title">Intensidad de vibración</string>
|
||||
@@ -266,6 +297,8 @@
|
||||
<string name="pref__keyboard__utility_key_enabled__label" comment="Preference title">Mostrar teclas de utilidad</string>
|
||||
<string name="pref__keyboard__utility_key_enabled__summary" comment="Preference summary">Muestra una tecla de utilidad configurable junto a la barra espaciadora</string>
|
||||
<string name="pref__keyboard__utility_key_action__label" comment="Preference title">Acción de tecla de utilidad</string>
|
||||
<string name="pref__keyboard__space_bar_mode__label" comment="Preference title">Etiqueta de la barra espaciadora</string>
|
||||
<string name="pref__keyboard__capitalization_behavior__label" comment="Preference title">Comportamiento de las mayúsculas</string>
|
||||
<string name="pref__keyboard__font_size_multiplier__label" comment="Preference title">Multiplicador del tamaño de fuente</string>
|
||||
<string name="pref__keyboard__group_layout__label" comment="Preference group title">Distribución</string>
|
||||
<string name="pref__keyboard__one_handed_mode__label" comment="Preference title">Modo a una mano</string>
|
||||
@@ -385,6 +418,7 @@
|
||||
<string name="pref__advanced__settings_theme__light" comment="Possible value of Settings theme preference in Advanced">Claro</string>
|
||||
<string name="pref__advanced__settings_theme__dark" comment="Possible value of Settings theme preference in Advanced">Oscuro</string>
|
||||
<string name="pref__advanced__settings_theme__amoled_dark" comment="Possible value of Settings theme preference in Advanced">AMOLED Oscuro</string>
|
||||
<string name="pref__advanced__settings_material_you__label" comment="Label of Material You preference in Advanced">Usar material tú</string>
|
||||
<string name="pref__advanced__settings_language__label" comment="Label of Settings language preference in Advanced">Idioma de ajustes</string>
|
||||
<string name="pref__advanced__show_app_icon__label" comment="Label of Show app icon preference in Advanced">Mostrar icono de la aplicación en el launcher</string>
|
||||
<string name="pref__advanced__show_app_icon__summary_atleast_q" comment="Summary of Show app icon preference in Advanced for Android 10+">Habilitado siempre en Android 10+ debido a restricciones del sistema</string>
|
||||
@@ -422,6 +456,12 @@
|
||||
<string name="setup__select_ime__title">Seleccionar {app_name}</string>
|
||||
<string name="setup__select_ime__description">{app_name} está ahora habilitado en su sistema. ¡Para usarlo activamente cambie a {app_name} seleccionándolo en el diálogo del selector de entrada!</string>
|
||||
<string name="setup__select_ime__switch_keyboard_btn">Cambiar teclado</string>
|
||||
<string name="setup__grant_notification_permission__title">Permitir notificaciones de informes de errores</string>
|
||||
<string name="setup__grant_notification_permission__description">A partir de Android 13+, las aplicaciones deben pedir permiso para
|
||||
enviar notificaciones. En Florisboard, esto solo se utiliza para abrir una pantalla de informe de error en caso de error.
|
||||
Este permiso puede cambiarse en cualquier momento en los ajustes del sistema.
|
||||
</string>
|
||||
<string name="setup__grant_notification_permission__btn">Conceder permiso</string>
|
||||
<string name="setup__finish_up__title">Finalizar</string>
|
||||
<string name="setup__finish_up__description_p1">{app_name} ahora está habilitado en su sistema y listo para ser personalizado.</string>
|
||||
<string name="setup__finish_up__description_p2">Si encuentra algún problema, errores, fallos o sólo desea hacer alguna sugerencia, ¡revise el repositorio del proyecto en el menú de \"Acerca de\"!</string>
|
||||
@@ -438,6 +478,7 @@
|
||||
<string name="backup_and_restore__back_up__files_ime_keyboard">Extensiones del teclado</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_spelling">Extensiones de corrección ortográfica / diccionarios</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_theme">Extensiones de temas</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history">Historial del portapapeles</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_text_items">Ítems de texto</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_image_items">Imágenes</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_video_items">Vídeos</string>
|
||||
@@ -514,6 +555,8 @@
|
||||
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__summary">Al borrar el clip principal también se borra la última entrada del historial</string>
|
||||
<string name="send_to_clipboard__unknown_error">Ha ocurrido un error desconocido. Por favor, vuelve a intentarlo.</string>
|
||||
<string name="send_to_clipboard__type_not_supported_error">Este tipo de medio no está soportado.</string>
|
||||
<string name="send_to_clipboard__android_version_to_old_error">La versión de android es demasiado antigua para esta función.</string>
|
||||
<string name="send_to_clipboard__description__copied_image_to_clipboard">Imagen copiada en el portapapeles.</string>
|
||||
<!-- Devtools strings -->
|
||||
<string name="devtools__title" comment="Title of Devtools screen. Translators: treat this string as 'Developer tools' for translation, except a similar short term is available for your language.">Herramientas de desarrollo</string>
|
||||
<string name="devtools__enabled__label" comment="Label of Enable developer tools in Devtools">Habilitar herramientas de desarrollador</string>
|
||||
@@ -541,6 +584,11 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Ajustes Secure de Android</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Ajustes System de Android</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Regiones del sistema</string>
|
||||
<string name="devtools__debuglog__title">Registro de depuración</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">Registro de depuración copiado en el portapapeles</string>
|
||||
<string name="devtools__debuglog__copy_log">Copiar registro</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Copiar registro (formato GitHub)</string>
|
||||
<string name="devtools__debuglog__loading">Cargando…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__meta__authors">Autores</string>
|
||||
<string name="ext__meta__components">Componentes agrupados</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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 -->
|
||||
@@ -26,10 +25,6 @@
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">برای دسترسی به تاریخ صورتکها گوشیتان باز کنید.</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>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">فلش بالا</string>
|
||||
<string name="quick_action__arrow_up__tooltip">فلش به سمت بالا</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojit</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Hymiöt</string>
|
||||
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaomojit</string>
|
||||
<string name="prefs__media__emoji_recently_used_max_size">Emojihistorian maksimikoko</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Emojien oletusihonväri</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Emojien oletushiustyyli</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Esineet</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Symbolit</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Liput</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Viimeaikaisia emojeja ei löytynyt. Kun käytät emojeja, ne näytetään automaattisesti täällä.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro tip: Paina emojia pitkään poistaaksesi sen tästä näkymästä!</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">Poistettu {emoji} viimeaikaisista emojeista</string>
|
||||
<!-- Quick action strings -->
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
|
||||
@@ -13,9 +13,18 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Émojis</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Émoticônes</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">Taille maximum de l\'historique d\'émojis</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Couleur de peau des émojis par défaut</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Cheveux des émojis par défaut</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">Suggestions d\'émoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">Activer les suggestions d\'émoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled__summary" comment="Preference summary">Fournit des suggestions d\'émoji lors de la saisie</string>
|
||||
<string name="prefs__media__emoji_suggestion_type" comment="Preference title">Type de déclencheur</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">Mettre à jour l\'historique émoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history__summary" comment="Preference summary">Ajoute les émojis suggérés et acceptés à l\'historique</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">Afficher le nom de l\'émoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name__summary" comment="Preference summary">Affiche le nom des suggestions d\'émoji à côté de l\'émoji</string>
|
||||
<string name="prefs__media__emoji_suggestion_query_min_length" comment="Preference title">Longueur minimale de la requête</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_max_count" comment="Preference title">Nombre maximal de candidats</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Smileys & Émotions</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Personnes & Corps</string>
|
||||
@@ -26,10 +35,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objets</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Symboles</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Drapeaux</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Aucun émoji récemment utilisé n\'a été trouvé. Une fois que vous aurez commencé à taper des émojis, ils apparaîtront automatiquement ici.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">Pour accéder à l\'historique emoji, veuillez d\'abord déverrouiller votre appareil.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Conseil de pro : appuyez longuement sur les émojis récemment utilisés pour les supprimer à nouveau de cette vue !</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">Suppression de {emoji} des emojis récemment utilisés</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Flèche haut</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Appui sur la flèche haut</string>
|
||||
@@ -124,6 +129,8 @@
|
||||
<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)">Au moins un champ n\'a pas de valeur sélectionnée. Veuillez choisir une valeur pour le(s) champ(s).</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} (non installé)</string>
|
||||
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Dispositions</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Confirmer la suppression</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">Êtes-vous sûr de vouloir supprimer ce sous-type ?</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Thème</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Mode du thème</string>
|
||||
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">Heure de lever du soleil</string>
|
||||
@@ -565,6 +572,8 @@
|
||||
<string name="devtools__show_input_state_overlay__summary" comment="Summary of Show input cache overlay in Devtools">Superpose l\'état actuel de l\'entrée pour le débogage</string>
|
||||
<string name="devtools__show_spelling_overlay__label" comment="Label of Show spelling overlay in Devtools">Afficher la superposition d\'orthographe</string>
|
||||
<string name="devtools__show_spelling_overlay__summary" comment="Summary of Show spelling overlay in Devtools">Superpose les résultats d\'orthographe actuels pour le débogage</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__label">Superposer l\'autocomplétion sur la ligne</string>
|
||||
<string name="devtools__show_inline_autofill_overlay__summary">Superpose les résultats d\'orthographe actuels pour le débogage</string>
|
||||
<string name="devtools__show_key_touch_boundaries__label" comment="Label of Show key touch boundaries in Devtools">Afficher les limites des touches</string>
|
||||
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">Soulignez en rouge les limites des touches clés</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">Afficher les aides de glisser-déposer</string>
|
||||
@@ -580,6 +589,11 @@
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Paramètres de sécurité Android</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Paramètres système Android</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Paramètres régionaux du système</string>
|
||||
<string name="devtools__debuglog__title">Journal de débogage</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">Journal de débogage copié dans le presse-papiers</string>
|
||||
<string name="devtools__debuglog__copy_log">Copier le journal</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Copier le journal (formatage GitHub)</string>
|
||||
<string name="devtools__debuglog__loading">Chargement…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Modules & Extensions</string>
|
||||
<string name="ext__list__ext_theme">Extensions de thème</string>
|
||||
@@ -749,6 +763,10 @@
|
||||
<string name="enum__emoji_hair_style__curly_hair" comment="Enum value label">{emoji} Cheveux frisés</string>
|
||||
<string name="enum__emoji_hair_style__white_hair" comment="Enum value label">{emoji} Cheveux blanc</string>
|
||||
<string name="enum__emoji_hair_style__bald" comment="Enum value label">{emoji} Chauve</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon">Deux points</string>
|
||||
<string name="enum__emoji_suggestion_type__leading_colon__description" comment="Keep the :emoji_name while translating, this is a syntax guide">Suggère des émojis en utilisant la syntaxe :nom</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text">Intégré au texte</string>
|
||||
<string name="enum__emoji_suggestion_type__inline_text__description">Suggère des émojis en tapant simplement son nom comme mot</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates" comment="Enum value label">Suggestions ci-dessus</string>
|
||||
<string name="enum__extended_actions_placement__above_candidates__description" comment="Enum value description">Place la rangée des actions étendues entre l\'interface utilisateur de l\'application et la rangée des suggestions</string>
|
||||
<string name="enum__extended_actions_placement__below_candidates" comment="Enum value label">Les suggestions ci-dessous</string>
|
||||
@@ -864,4 +882,8 @@
|
||||
<item quantity="one">{v} élément</item>
|
||||
<item quantity="other">{v} éléments</item>
|
||||
</plurals>
|
||||
<plurals name="unit__characters__written">
|
||||
<item quantity="one">{v} caractère</item>
|
||||
<item quantity="other">{v} caractères</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emotikoni</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emotikoni</string>
|
||||
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaoemotikoni</string>
|
||||
<string name="prefs__media__emoji_recently_used_max_size">Prikazana povijest korištenih emotikona je maksimalna</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Željeni ton boje emotikona</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Željeni stil frizure emotikona</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objekti</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Simboli</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Zastave</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Nema nedavno korištenih emotikona. Čim ih počnete koristiti, oni će se pojaviti ovdje.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro savjet: Dugo držite nedavno korištene emotikone kako bi ste ih uklonili sa popisa nedavno korištenih emotikona!</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} je ukonjen iz nedavno korištenih emotikona</string>
|
||||
<!-- Quick action strings -->
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
<string name="one_handed__move_start_btn_content_description" comment="Content description for the one-handed move to left button">Billentyűzet balra helyezése.</string>
|
||||
<string name="one_handed__move_end_btn_content_description" comment="Content description for the one-handed move to right button">Billentyűzet jobbra helyezése.</string>
|
||||
<!-- Media strings -->
|
||||
<string name="settings__media__title">Emojik</string>
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojik</string>
|
||||
<string name="settings__media__title">Emodzsik</string>
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emodzsik</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Emotikonok</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">Emojielőzmények maximális mérete</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Előnyben részesített emoji-bőrszín</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Előnyben részesített emoji hajstílus</string>
|
||||
<string name="media__tab__kaomoji" comment="Tab description for kaomoji in the media UI">Kaomodzsi</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Emodzsik előnyben részesített bőrszíne</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Emodzsik előnyben részesítet hajstílusa</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Mosolyok és érzelmek</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Emberek és test</string>
|
||||
@@ -26,10 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Tárgyak</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Szimbólumok</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Zászlók</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Nem található mostanában használt emoji. Az emojik automatikusan megjelennek itt, amint elkezdi őket beírni.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">A hangulatjel előzmények eléréséhez, kérjük először oldja fel a készülékét.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Profi tipp: nyomjon hosszan a mostanában használt emojikra, hogy eltávolítsa őket ebből a nézetből!</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} eltávolítva a mostanában használt emojik közül</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Felfele nyíl</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Felfelé nyíl végrehajtása</string>
|
||||
@@ -40,37 +35,37 @@
|
||||
<string name="quick_action__arrow_right" maxLength="12">Jobbra nyíl</string>
|
||||
<string name="quick_action__arrow_right__tooltip">Jobbra nyíl végrehajtása</string>
|
||||
<string name="quick_action__clipboard_clear_primary_clip" maxLength="12">Vágólapürítő</string>
|
||||
<string name="quick_action__clipboard_clear_primary_clip__tooltip">Elsődleges vágólap ürítés végrehajtása</string>
|
||||
<string name="quick_action__clipboard_clear_primary_clip__tooltip">Elsődleges vágólap ürítésének végrehajtása</string>
|
||||
<string name="quick_action__clipboard_copy" maxLength="12">Másolás</string>
|
||||
<string name="quick_action__clipboard_copy__tooltip">Vágólapi másolás végrehajtása</string>
|
||||
<string name="quick_action__clipboard_copy__tooltip">Vágólapra másolás végrehajtása</string>
|
||||
<string name="quick_action__clipboard_cut" maxLength="12">Kivágás</string>
|
||||
<string name="quick_action__clipboard_cut__tooltip">Vágólapi kivágás végrehajtása</string>
|
||||
<string name="quick_action__clipboard_cut__tooltip">Vágólapra kivágás végrehajtása</string>
|
||||
<string name="quick_action__clipboard_paste" maxLength="12">Beillesztés</string>
|
||||
<string name="quick_action__clipboard_paste__tooltip">Vágólapi beillesztés végrehajtása</string>
|
||||
<string name="quick_action__clipboard_paste__tooltip">Vágólapról beillesztés végrehajtása</string>
|
||||
<string name="quick_action__clipboard_select_all" maxLength="12">Mind kijelöl</string>
|
||||
<string name="quick_action__clipboard_select_all__tooltip">Összes kijelölése vágólapra végrehajtása</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard" maxLength="12">Vágólap</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard__tooltip">Vágólapelőzmények megnyitása</string>
|
||||
<string name="quick_action__ime_ui_mode_media" maxLength="12">Hangulatjel</string>
|
||||
<string name="quick_action__ime_ui_mode_media__tooltip">Hangulatjel panel megnyitása</string>
|
||||
<string name="quick_action__ime_ui_mode_clipboard__tooltip">Vágólap-előzmények megnyitása</string>
|
||||
<string name="quick_action__ime_ui_mode_media" maxLength="12">Emodzsi</string>
|
||||
<string name="quick_action__ime_ui_mode_media__tooltip">Emodzsi panel megnyitása</string>
|
||||
<string name="quick_action__settings" maxLength="12">Beállítások</string>
|
||||
<string name="quick_action__settings__tooltip">Beállítások megnyitása</string>
|
||||
<string name="quick_action__undo" maxLength="12">Visszavonás</string>
|
||||
<string name="quick_action__undo__tooltip">Utolsó változás visszavonása</string>
|
||||
<string name="quick_action__undo__tooltip">Utolsó bemenet visszavonása</string>
|
||||
<string name="quick_action__redo" maxLength="12">Újra</string>
|
||||
<string name="quick_action__redo__tooltip">Visszavonás törlése</string>
|
||||
<string name="quick_action__redo__tooltip">Utolsó bemenet újbóli beírása</string>
|
||||
<string name="quick_action__toggle_actions_overflow" maxLength="12">Továbbiak</string>
|
||||
<string name="quick_action__toggle_actions_overflow__tooltip">További műveletek megjelenítése vagy elrejtése</string>
|
||||
<string name="quick_action__toggle_incognito_mode" maxLength="12">Inkognitó</string>
|
||||
<string name="quick_action__toggle_incognito_mode__tooltip">Inkognitó mód bekapcsolása</string>
|
||||
<string name="quick_action__toggle_autocorrect" maxLength="12">Auto-korrekt</string>
|
||||
<string name="quick_action__toggle_autocorrect__tooltip">Automatikus javítás váltása</string>
|
||||
<string name="quick_action__toggle_autocorrect__tooltip">Automatikus javítás be/ki</string>
|
||||
<string name="quick_action__voice_input" maxLength="12">Hangbevitel</string>
|
||||
<string name="quick_action__voice_input__tooltip" comment="IME stands for Input Method Editor and is indirectly equivalent to 'keyboard'.">Hangbevitel szolgáltató megnyitása</string>
|
||||
<string name="quick_action__voice_input__tooltip" comment="IME stands for Input Method Editor and is indirectly equivalent to 'keyboard'.">Hangbevitel szolgáltatójának megnyitása</string>
|
||||
<string name="quick_action__one_handed_mode" maxLength="12">Egykezes</string>
|
||||
<string name="quick_action__one_handed_mode__tooltip">Egykezes mód váltása</string>
|
||||
<string name="quick_action__drag_marker" maxLength="12" comment="This action is only used as a placeholder in the actions editor drag and drop screen and only visible in debug mode">Húzás jelölő</string>
|
||||
<string name="quick_action__drag_marker__tooltip" comment="This action is only used as a placeholder in the actions editor drag and drop screen and only visible in debug mode">Jelenlegi kurzor mozgató pozíció</string>
|
||||
<string name="quick_action__one_handed_mode__tooltip">Egykezes mód be/ki</string>
|
||||
<string name="quick_action__drag_marker" maxLength="12" comment="This action is only used as a placeholder in the actions editor drag and drop screen and only visible in debug mode">Húzásjelölő</string>
|
||||
<string name="quick_action__drag_marker__tooltip" comment="This action is only used as a placeholder in the actions editor drag and drop screen and only visible in debug mode">Jelenlegi húzásjelölő pozíciója</string>
|
||||
<string name="quick_action__noop" maxLength="12" comment="Noop=no operation; this action is only used as a placeholder in the actions editor drag and drop screen">Semmi</string>
|
||||
<string name="quick_action__noop__tooltip" comment="Noop=no operation; this action is only used as a placeholder in the actions editor drag and drop screen">Nincs művelet</string>
|
||||
<string name="quick_actions_overflow__customize_actions_button">Műveletek átrendezése</string>
|
||||
@@ -79,17 +74,17 @@
|
||||
<string name="quick_actions_editor__subheader_dynamic_actions">Dinamikus műveletek ({n})</string>
|
||||
<string name="quick_actions_editor__subheader_hidden_actions">Elrejtett műveletek ({n})</string>
|
||||
<!-- Incognito mode strings -->
|
||||
<string name="incognito_mode__toast_after_enabled">Az inkognitó mód bekapcsolva\n\n{app_name} nem fog tanulni a beírt szavakból, amíg ez a mód aktív</string>
|
||||
<string name="incognito_mode__toast_after_enabled">Az inkognitó mód bekapcsolva\n\nA {app_name} nem fog tanulni a beírt szavakból, amíg ez a mód aktív</string>
|
||||
<string name="incognito_mode__toast_after_disabled">Az inkognitó mód mostantól alapértelmezés szerint ki van kapcsolva</string>
|
||||
<!-- Settings UI strings -->
|
||||
<string name="settings__title" comment="Title of Settings">Beállítások</string>
|
||||
<string name="settings__preview_keyboard" comment="Hint for try your setup box">Próbálja ki a beállításait</string>
|
||||
<string name="settings__help" comment="General label for help buttons in Settings">Súgó</string>
|
||||
<string name="settings__default" comment="General string which is used when a preference has the default value set">Alapértelmezett</string>
|
||||
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">Rendszer alapértelmezett</string>
|
||||
<string name="settings__home__title" comment="Title of the Home screen">A {app_name} üdvözli</string>
|
||||
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">FlorisBoard nincs engedélyezve a rendszerben, így nem lesz elérhető beviteli módszerként a bevitelválasztóban. A probléma megoldásához kattintson ide.</string>
|
||||
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">A FlorisBoard nincs kiválasztva alapértelmezett beviteli módszerként. A probléma megoldásához kattintson ide.</string>
|
||||
<string name="settings__system_default" comment="General string which is used when a preference has the system default value set">Rendszer alapértelmezése</string>
|
||||
<string name="settings__home__title" comment="Title of the Home screen">Üdvözli a {app_name}</string>
|
||||
<string name="settings__home__ime_not_enabled" comment="Error message shown in Home fragment when FlorisBoard is not enabled in the system">A FlorisBoard nincs engedélyezve a rendszerben, így nem lesz elérhető beviteli módként a bevitelválasztóban. A probléma megoldásához kattintson ide.</string>
|
||||
<string name="settings__home__ime_not_selected" comment="Warning message shown in Home fragment when FlorisBoard is not selected as the default keyboard">Nem a FlorisBoard az alapértelmezett beviteli mód. A probléma megoldásához kattintson ide.</string>
|
||||
<string name="settings__localization__title" comment="Title of languages and Layout screen">Nyelvek és elrendezések</string>
|
||||
<string name="settings__localization__display_language_names_in__label" comment="Label of Display language names in preference">Nyelvek neveinek megjelenítése ebben</string>
|
||||
<string name="settings__localization__group_subtypes__label" comment="Label of subtypes group">Altípusok</string>
|
||||
@@ -104,7 +99,7 @@
|
||||
<string name="settings__localization__subtype_symbols_layout" comment="Label for layout dropdown in subtype dialog">Elsődleges szimbólumelrendezés</string>
|
||||
<string name="settings__localization__subtype_symbols2_layout" comment="Label for layout dropdown in subtype dialog">Másodlagos szimbólumelrendezés</string>
|
||||
<string name="settings__localization__subtype_composer" comment="Label for composer dropdown in subtype dialog.">Összeállító</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).">Pénznem készlet</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).">Pénznemkészlet</string>
|
||||
<string name="settings__localization__subtype_numeric_layout" comment="Label for layout dropdown in subtype dialog">Számelrendezés</string>
|
||||
<string name="settings__localization__subtype_numeric_advanced_layout" comment="Label for layout dropdown in subtype dialog">(Speciális) számelrendezés</string>
|
||||
<string name="settings__localization__subtype_numeric_row_layout" comment="Label for layout dropdown in subtype dialog">Számsor elrendezése</string>
|
||||
@@ -112,20 +107,22 @@
|
||||
<string name="settings__localization__subtype_phone2_layout" comment="Label for layout dropdown in subtype dialog">Másodlagos telefonelrendezés</string>
|
||||
<string name="settings__localization__subtype_select_locale" comment="Subtype select language title">Nyelv választása</string>
|
||||
<string name="settings__localization__subtype_search_locale_placeholder" comment="Subtype search language placeholder">Nyelv keresése</string>
|
||||
<string name="settings__localization__subtype_search_locale_not_found" comment="Subtype search language not found">Nem sikerült egyező nyelvet találni \"{search_term}\".</string>
|
||||
<string name="settings__localization__subtype_select_placeholder" comment="Subtype dialog select value placeholder (&#8210; is a hyphen character)">‒ &#8210 kiválasztása;</string>
|
||||
<string name="settings__localization__subtype_search_locale_not_found" comment="Subtype search language not found">Nem található ennek megfelelő nyelv: „{search_term}”.</string>
|
||||
<string name="settings__localization__subtype_select_placeholder" comment="Subtype dialog select value placeholder (&#8210; is a hyphen character)">‒ válasszon ‒</string>
|
||||
<string name="settings__localization__subtype_summary" comment="Subtype summary">{characters_name} / {symbols_name} / {currency_set_name}</string>
|
||||
<string name="settings__localization__suggested_subtype_presets" comment="Suggested presets title">Javasolt altípusok sémák</string>
|
||||
<string name="settings__localization__suggested_subtype_presets_none_found" comment="Suggested presets none found">Nem elérhető javasolt séma. Használja a lentebbi gombot az összes altípus séma megtekintéséhez.</string>
|
||||
<string name="settings__localization__subtype_presets" comment="Subtype presets dialog title">Altípus sémák</string>
|
||||
<string name="settings__localization__suggested_subtype_presets" comment="Suggested presets title">Javasolt altípussémák</string>
|
||||
<string name="settings__localization__suggested_subtype_presets_none_found" comment="Suggested presets none found">Nem elérhető javasolt séma. Használja a lentebbi gombot az összes altípusséma megtekintéséhez.</string>
|
||||
<string name="settings__localization__subtype_presets" comment="Subtype presets dialog title">Altípussémák</string>
|
||||
<string name="settings__localization__subtype_presets_view_all" comment="View all presets button">Összes megjelenítése</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">Úgy tűnik, hogy nem állított be altípusokat. Tartalékként az angol/QWERTY lesz használva!</string>
|
||||
<string name="settings__localization__subtype_no_subtypes_configured_warning" comment="Warning message that no subtype has been defined">Úgy tűnik, hogy nem állított be altípusokat. Tartalékként az angol/QWERTY altípus lesz használva.</string>
|
||||
<string name="settings__localization__subtype_error_already_exists" comment="Error message shown in subtype dialog when a subtype to add already exists">Ez az altípus már létezik!</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)">Legalább egy mezőnek nincs érték kiválasztva. Kérjük válasszon értéket az összes mezőnek.</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} (nincs telepítve)</string>
|
||||
<string name="settings__localization__group_layouts__label" comment="Label of layouts group">Kiosztások</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">Törlés megerősítése</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">Biztosan törli ezt az altípust?</string>
|
||||
<string name="settings__theme__title" comment="Title of the Theme screen">Téma</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Téma mód</string>
|
||||
<string name="pref__theme__mode__label" comment="Label of the theme mode preference">Témamód</string>
|
||||
<string name="pref__theme__sunrise_time__label" comment="Label of the sunrise time preference">Napkelte ideje</string>
|
||||
<string name="pref__theme__sunset_time__label" comment="Label of the sunset time preference">Napnyugta ideje</string>
|
||||
<string name="pref__theme__day" comment="Label of the day group (day means light theme)">Nappali téma</string>
|
||||
@@ -135,17 +132,17 @@
|
||||
<string name="pref__theme__source_internal" comment="Label for the theme source field">Belső tárhely</string>
|
||||
<string name="pref__theme__source_external" comment="Label for the theme source field">Külső szolgáltató</string>
|
||||
<string name="settings__theme_manager__title_day" comment="Title of the theme manager screen for day theme selection">Nappali téma kiválasztása</string>
|
||||
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">Éjjeli téma kiválasztása</string>
|
||||
<string name="settings__theme_manager__title_night" comment="Title of the theme manager screen for night theme selection">Éjszakai téma kiválasztása</string>
|
||||
<string name="settings__theme_editor__fine_tune__title">Finomhangoló szerkesztő</string>
|
||||
<string name="settings__theme_editor__fine_tune__level">Szerkesztői szint</string>
|
||||
<string name="settings__theme_editor__fine_tune__display_colors_as">Színek megjelenítése mint</string>
|
||||
<string name="settings__theme_editor__fine_tune__display_kbd_after_dialogs">Billentyűzet megjelenítése párbeszédpanelek után</string>
|
||||
<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__no_rules_defined">Ennél a stíluslapnál nincsenek definiált szabályok. Adjon hozzá új szabályt a stíluslap testreszabásához.</string>
|
||||
<string name="settings__theme_editor__rule_already_exists">Ez a stíluslap szabály már korábban meghatározásra került.</string>
|
||||
<string name="settings__theme_editor__rule_element">Cél elem</string>
|
||||
<string name="settings__theme_editor__rule_codes">Cél billentyűkódok</string>
|
||||
<string name="settings__theme_editor__no_rules_defined">A stíluslapnak nincsenek megadott szabályai. Adjon hozzá egy új szabályt a stíluslap testreszabásához.</string>
|
||||
<string name="settings__theme_editor__rule_already_exists">Ez a stíluslapszabály már korábban meg lett adva.</string>
|
||||
<string name="settings__theme_editor__rule_element">Célelem</string>
|
||||
<string name="settings__theme_editor__rule_codes">Célbillentyűkódok</string>
|
||||
<string name="settings__theme_editor__rule_groups">Csoportok</string>
|
||||
<string name="settings__theme_editor__rule_shift_states">Shift állapotok</string>
|
||||
<string name="settings__theme_editor__rule_selectors">Választók</string>
|
||||
@@ -153,7 +150,7 @@
|
||||
<string name="settings__theme_editor__edit_code">Billentyűkód szerkesztése</string>
|
||||
<string name="settings__theme_editor__no_codes_defined">Szabály alkalmazása az összes célelemre.</string>
|
||||
<string name="settings__theme_editor__codes_defined">Szabály alkalmazása csak a következő billentyűkódokkal rendelkező elemekre:</string>
|
||||
<string name="settings__theme_editor__code_already_exists">Ez a billentyűkód már definiálva van.</string>
|
||||
<string name="settings__theme_editor__code_already_exists">Ez a billentyűkód már meg lett adva.</string>
|
||||
<string name="settings__theme_editor__code_invalid">Ez a billentyűkód nem érvényes. Győződjön meg róla, hogy az érték {c_min} és {c_max} között van, vagy {i_min} és {i_max} között a belső speciális billentyűk esetében.</string>
|
||||
<string name="settings__theme_editor__code_help_text">Esetlegesen az alábbi linkek segíteni fognak a megfelelő billentyűkód megtalálásában:</string>
|
||||
<string name="settings__theme_editor__code_placeholder">Kód</string>
|
||||
@@ -164,7 +161,7 @@
|
||||
<string name="settings__theme_editor__code_recording_placeholder">Rögzítés…</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>
|
||||
<string name="settings__theme_editor__property_already_exists">Már létezik ilyen nevű tulajdonság a jelenlegi szabályban.</string>
|
||||
<string name="settings__theme_editor__property_name">Tulajdonság neve</string>
|
||||
<string name="settings__theme_editor__property_value">Tulajdonság értéke</string>
|
||||
<string name="settings__theme_editor__property_value_shape_apply_for_all_corners">Alkalmazás az összes sarokra</string>
|
||||
@@ -176,26 +173,26 @@
|
||||
<string name="snygg__rule_element__defines_description">Definiálja a szabályon belüli változókat, hogy újrahasználhasson gyakori színeket és sorokat a stíluslapon.</string>
|
||||
<string name="snygg__rule_element__keyboard">Billentyűzetablak</string>
|
||||
<string name="snygg__rule_element__key">Billentyű</string>
|
||||
<string name="snygg__rule_element__key_hint">Billentyű tipp</string>
|
||||
<string name="snygg__rule_element__key_hint">Billentyű eszköztippje</string>
|
||||
<string name="snygg__rule_element__key_popup">Felugró billentyű</string>
|
||||
<string name="snygg__rule_element__clipboard_header">Vágólap fejléc</string>
|
||||
<string name="snygg__rule_element__clipboard_item">Vágólap elem</string>
|
||||
<string name="snygg__rule_element__clipboard_header">Vágólapfejléc</string>
|
||||
<string name="snygg__rule_element__clipboard_item">Vágólapelem</string>
|
||||
<string name="snygg__rule_element__clipboard_item_popup">Felugró vágólapelem</string>
|
||||
<string name="snygg__rule_element__emoji_key">Emodzsi gomb</string>
|
||||
<string name="snygg__rule_element__emoji_key_popup">Emodzsi felugró gomb</string>
|
||||
<string name="snygg__rule_element__emoji_key_tab">Emodzsi fül</string>
|
||||
<string name="snygg__rule_element__emoji_key_tab">Emodzsi lap</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_layout">Fekvő bemenetelrendezés</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_field">Fekvő bemenet mező</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_action">Fekvő bemenet művelet</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_field">Fekvő bemeneti mező</string>
|
||||
<string name="snygg__rule_element__extracted_landscape_input_action">Fekvő bemeneti művelet</string>
|
||||
<string name="snygg__rule_element__glide_trail">Csúsztatási nyomvonal</string>
|
||||
<string name="snygg__rule_element__incognito_mode_indicator">Inkognitó mód jelző</string>
|
||||
<string name="snygg__rule_element__incognito_mode_indicator">Inkognitó mód jelzője</string>
|
||||
<string name="snygg__rule_element__one_handed_panel">Egykezes panel</string>
|
||||
<string name="snygg__rule_element__smartbar">Okossáv</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_row">Okossáv megosztott műveletek sora</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_toggle">Okossáv megosztott műveletek váltó</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_row">Okossáv bővített műveletek sora</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_toggle">Okossáv bővített műveletek váltó</string>
|
||||
<string name="snygg__rule_element__smartbar_action_key">Okossáv művelet gomb</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_row">Okossáv megosztott műveleteinek sora</string>
|
||||
<string name="snygg__rule_element__smartbar_shared_actions_toggle">Okossáv megosztott műveletei be/ki</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_row">Okossáv bővített műveleteinek sora</string>
|
||||
<string name="snygg__rule_element__smartbar_extended_actions_toggle">Okossáv bővített műveletei be/ki</string>
|
||||
<string name="snygg__rule_element__smartbar_action_key">Okossáv műveletgombja</string>
|
||||
<string name="snygg__rule_element__smartbar_action_tile">Okossáv művelet csempe</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_overflow">Okossáv művelet túlfolyó</string>
|
||||
<string name="snygg__rule_element__smartbar_actions_overflow_customize_button">Okossáv túlfolyó műveleteinek testreszabó gombja</string>
|
||||
@@ -206,7 +203,7 @@
|
||||
<string name="snygg__rule_element__smartbar_candidate_word">Okossáv javaslat szó</string>
|
||||
<string name="snygg__rule_element__smartbar_candidate_clip">Okossáv javaslat vágólapról</string>
|
||||
<string name="snygg__rule_element__smartbar_candidate_spacer">Okossáv javaslat elválasztó</string>
|
||||
<string name="snygg__rule_element__system_nav_bar">Rendszer navigációs sáv</string>
|
||||
<string name="snygg__rule_element__system_nav_bar">Rendszer navigációs sávja</string>
|
||||
<string name="snygg__rule_selector__pressed">Lenyomott</string>
|
||||
<string name="snygg__rule_selector__focus">Fókuszált</string>
|
||||
<string name="snygg__rule_selector__disabled">Letiltott</string>
|
||||
@@ -214,14 +211,14 @@
|
||||
<string name="snygg__property_name__height">Magasság</string>
|
||||
<string name="snygg__property_name__background">Háttér</string>
|
||||
<string name="snygg__property_name__foreground">Előtér</string>
|
||||
<string name="snygg__property_name__border_color">Szegély szín</string>
|
||||
<string name="snygg__property_name__border_style">Szegély stílus</string>
|
||||
<string name="snygg__property_name__border_width">Szegély szélessége</string>
|
||||
<string name="snygg__property_name__border_color">Szegélyszín</string>
|
||||
<string name="snygg__property_name__border_style">Szegélystílus</string>
|
||||
<string name="snygg__property_name__border_width">Szegélyszélesség</string>
|
||||
<string name="snygg__property_name__font_family">Betűkészlet</string>
|
||||
<string name="snygg__property_name__font_size">Betűméret</string>
|
||||
<string name="snygg__property_name__font_style">Betűstílus</string>
|
||||
<string name="snygg__property_name__font_variant">Betű változat</string>
|
||||
<string name="snygg__property_name__font_weight">Betű vastagság</string>
|
||||
<string name="snygg__property_name__font_variant">Betűváltozat</string>
|
||||
<string name="snygg__property_name__font_weight">Betűvastagság</string>
|
||||
<string name="snygg__property_name__shadow_elevation">Árnyék kiemelés</string>
|
||||
<string name="snygg__property_name__shape">Forma</string>
|
||||
<string name="snygg__property_name__var_primary">Elsődleges szín</string>
|
||||
@@ -392,22 +389,22 @@
|
||||
<string name="pref__glide__immediate_backspace_deletes_word__summary">A csúsztatás után egyből megnyomott törlés gomb az egész szót törli</string>
|
||||
<string name="pref__gestures__general_title" comment="Preference group title">Általános gesztusok</string>
|
||||
<string name="pref__gestures__space_bar_title" comment="Preference group title">Szóköz gesztusok</string>
|
||||
<string name="pref__gestures__other_title" comment="Preference group title">Egyéb gesztusok / gesztusküszöb</string>
|
||||
<string name="pref__gestures__other_title" comment="Preference group title">Egyéb gesztusok / gesztusküszöbök</string>
|
||||
<string name="pref__gestures__swipe_up__label" comment="Preference title">Felhúzás</string>
|
||||
<string name="pref__gestures__swipe_down__label" comment="Preference title">Lehúzás</string>
|
||||
<string name="pref__gestures__swipe_left__label" comment="Preference title">Balra húzás</string>
|
||||
<string name="pref__gestures__swipe_right__label" comment="Preference title">Jobbra húzás</string>
|
||||
<string name="pref__gestures__space_bar_swipe_up__label" comment="Preference title">Szóköz felhúzás</string>
|
||||
<string name="pref__gestures__space_bar_swipe_left__label" comment="Preference title">Szóköz balra húzás</string>
|
||||
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">Szóköz jobbra húzás</string>
|
||||
<string name="pref__gestures__space_bar_swipe_up__label" comment="Preference title">Szóköz felhúzása</string>
|
||||
<string name="pref__gestures__space_bar_swipe_left__label" comment="Preference title">Szóköz balra húzása</string>
|
||||
<string name="pref__gestures__space_bar_swipe_right__label" comment="Preference title">Szóköz jobbra húzása</string>
|
||||
<string name="pref__gestures__space_bar_long_press__label" comment="Preference title">Szóköz hosszú lenyomása</string>
|
||||
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Törlés gomb balra húzás</string>
|
||||
<string name="pref__gestures__delete_key_swipe_left__label" comment="Preference title">Törlés gomb balra húzása</string>
|
||||
<string name="pref__gestures__delete_key_long_press__label" comment="Preference title">Törlés gomb hosszan nyomása</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">Húzás sebesség küszöb</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Húzás távolság küszöb</string>
|
||||
<string name="pref__gestures__swipe_velocity_threshold__label" comment="Preference title">Húzási sebesség küszöbe</string>
|
||||
<string name="pref__gestures__swipe_distance_threshold__label" comment="Preference title">Húzási távolság küszöbe</string>
|
||||
<string name="settings__advanced__title" comment="Title of Advanced settings">Speciális</string>
|
||||
<string name="pref__advanced__settings_theme__label" comment="Label of Settings theme preference in Advanced">Beállítások témája</string>
|
||||
<string name="pref__advanced__settings_theme__auto_amoled" comment="Possible value of Settings theme preference in Advanced">Rendszer alapértelmezett (AMOLED)</string>
|
||||
<string name="pref__advanced__settings_theme__auto_amoled" comment="Possible value of Settings theme preference in Advanced">Rendszer alapértelmezése (AMOLED)</string>
|
||||
<string name="pref__advanced__settings_theme__light" comment="Possible value of Settings theme preference in Advanced">Világos</string>
|
||||
<string name="pref__advanced__settings_theme__dark" comment="Possible value of Settings theme preference in Advanced">Sötét</string>
|
||||
<string name="pref__advanced__settings_theme__amoled_dark" comment="Possible value of Settings theme preference in Advanced">AMOLED sötét</string>
|
||||
@@ -430,11 +427,11 @@
|
||||
<string name="about__changelog__summary" comment="Preference summary">Újdonságok</string>
|
||||
<string name="about__repository__title" comment="Preference title">Kódtár (GitHub)</string>
|
||||
<string name="about__repository__summary" comment="Preference summary">Forráskód, megbeszélések, hibák és információ</string>
|
||||
<string name="about__privacy_policy__title" comment="Preference title">Adatvédelmi Irányelvek</string>
|
||||
<string name="about__privacy_policy__title" comment="Preference title">Adatvédelmi irányelvek</string>
|
||||
<string name="about__privacy_policy__summary" comment="Preference summary">A projekt adatvédelmi irányelvei</string>
|
||||
<string name="about__project_license__title" comment="Preference title">Projekt licenc</string>
|
||||
<string name="about__project_license__summary" comment="Preference summary">A FlorisBoard ez alatt van licencelve: {license_name}</string>
|
||||
<string name="about__project_license__error_license_text_failed" comment="Error text for license text loading failure">Hiba: Hiba a licenc szöveg betöltéskor.\n-> Ok: {error_message}</string>
|
||||
<string name="about__project_license__title" comment="Preference title">Projekt licence</string>
|
||||
<string name="about__project_license__summary" comment="Preference summary">A FlorisBoard {license_name} licenc alatt érhető el</string>
|
||||
<string name="about__project_license__error_license_text_failed" comment="Error text for license text loading failure">Hiba: A licenc szövegének betöltése sikertelen.\n-> Ok: {error_message}</string>
|
||||
<string name="about__project_license__error_reason_asset_manager_null" comment="Error text if asset manager is null">Az elemkezelő hivatkozás üres</string>
|
||||
<string name="about__third_party_licenses__title" comment="Preference title">Harmadik felek licencei</string>
|
||||
<string name="about__third_party_licenses__summary" comment="Preference summary">Az alkalmazásban szereplő harmadik felektől származó könyvtárak licencei</string>
|
||||
@@ -451,9 +448,9 @@
|
||||
<string name="setup__select_ime__switch_keyboard_btn">Billentyűzet váltása</string>
|
||||
<string name="setup__grant_notification_permission__title">Összeomlási jelentések értesítéseinek engedélyezése</string>
|
||||
<string name="setup__grant_notification_permission__description">Az Android 13+ rendszertől kezdve az alkalmazásoknak engedélyt kell kérniük az
|
||||
értesítések küldéséhez.
|
||||
A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon az összeomlási jelentés képernyője.
|
||||
Ez az engedély bármikor megváltoztatható a rendszerbeállításokban.
|
||||
értesítések küldéséhez. A Florisboardban ez csak arra szolgál, hogy
|
||||
összeomlás esetén az összeomlási jelentés képernyője. Ez az engedély
|
||||
bármikor megváltoztatható a rendszerbeállításokban.
|
||||
</string>
|
||||
<string name="setup__grant_notification_permission__btn">Engedély megadása</string>
|
||||
<string name="setup__finish_up__title">Befejezés</string>
|
||||
@@ -461,7 +458,7 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="setup__finish_up__description_p2">Ha bármilyen hibával vagy összeomlással találkozik, vagy csak javaslatot szeretne tenni, nézze meg a projekt kódtárját a névjegy képernyőn!</string>
|
||||
<string name="setup__finish_up__finish_btn">Személyre szabás megkezdése</string>
|
||||
<!-- Back up & Restore -->
|
||||
<string name="backup_and_restore__title">Mentés és visszaállítás</string>
|
||||
<string name="backup_and_restore__title">Mentés és helyreállítás</string>
|
||||
<string name="backup_and_restore__back_up__title">Adatok biztonsági mentése</string>
|
||||
<string name="backup_and_restore__back_up__summary">Biztonsági mentés arcívum generálása a beállításokból és a személyre szabásokból</string>
|
||||
<string name="backup_and_restore__back_up__destination">Biztonsági mentés céljának kiválasztása</string>
|
||||
@@ -469,24 +466,24 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="backup_and_restore__back_up__destination_share_intent">Más alkalmazás a megosztás menün keresztül</string>
|
||||
<string name="backup_and_restore__back_up__files">Válassza ki, hogy miről készít biztonsági mentést</string>
|
||||
<string name="backup_and_restore__back_up__files_jetpref_datastore">Beállítások</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_keyboard">Billentyűzet kiterjesztések</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_keyboard">Billentyűzetkiterjesztések</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_spelling">Helyesírási kiterjesztések / szótárak</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_theme">Téma kiterjesztések</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_theme">Témakiterjesztések</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history">Vágólapelőzmények</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_text_items">Szövegelemek</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_image_items">Képek</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_video_items">Videók</string>
|
||||
<string name="backup_and_restore__back_up__success">Biztonsági mentés archívum sikeresen exportálva!</string>
|
||||
<string name="backup_and_restore__back_up__failure">Nem sikerült exportálni a biztonsági mentés archívumot: {error_message}</string>
|
||||
<string name="backup_and_restore__restore__title">Adatok visszaállítása</string>
|
||||
<string name="backup_and_restore__restore__summary">Beállítások és személyre szabások visszatöltése mentett archívumból</string>
|
||||
<string name="backup_and_restore__restore__files">Válassza ki, hogy mit szeretne visszaállítani</string>
|
||||
<string name="backup_and_restore__restore__metadata">Kiválasztott biztonsági mentés archívum</string>
|
||||
<string name="backup_and_restore__back_up__success">Biztonsági mentési archívum sikeresen exportálva!</string>
|
||||
<string name="backup_and_restore__back_up__failure">Nem sikerült exportálni a biztonsági mentési archívumot: {error_message}</string>
|
||||
<string name="backup_and_restore__restore__title">Adatok helyreállítása</string>
|
||||
<string name="backup_and_restore__restore__summary">Beállítások és testreszabások visszaállítása mentett archívumból</string>
|
||||
<string name="backup_and_restore__restore__files">Válassza ki, hogy mit szeretne helyreállítani</string>
|
||||
<string name="backup_and_restore__restore__metadata">Kiválasztott biztonsági mentési archívum</string>
|
||||
<string name="backup_and_restore__restore__metadata_warn_different_version">Ez az archívum egy korábbi verzióból került mentésre, amely általában támogatott. Azonban felmerülhetnek problémák, vagy egyes beállítások nem feltétlenül töltődnek vissza az eltérő funkciók miatt.</string>
|
||||
<string name="backup_and_restore__restore__metadata_warn_different_vendor">Az archívum egy harmadik fél által kibocsátott alkalmazással készült, amely általában támogatott. Felmerülhet adatvesztés, ezért csak saját felelősségre ajánlott!</string>
|
||||
<string name="backup_and_restore__restore__metadata_error_invalid_metadata">Ez az archívum nem megfelelő metaadatot tartalmaz. Vagy megsérült, vagy módosításra került. Nem lehetséges a betöltés, kérjük válasszon egy másikat.</string>
|
||||
<string name="backup_and_restore__restore__metadata_error_nothing_to_restore">Ez az archívum nem tartalmaz visszatölthető adatot, kérjük válasszon másikat.</string>
|
||||
<string name="backup_and_restore__restore__mode">Visszaállítási mód</string>
|
||||
<string name="backup_and_restore__restore__metadata_warn_different_vendor">Az archívum egy harmadik féltől származó alkalmazással készült, amely általában nem támogatott. Adatvesztés történhet, ezért csak a saját felelősségére állítsa helyre.</string>
|
||||
<string name="backup_and_restore__restore__metadata_error_invalid_metadata">Ez az archívum nem megfelelő metaadatokat tartalmaz. Megsérült, vagy hibásan lett módosítva. Az archívumból való helyreállítás nem lehetséges, válasszon egy másikat.</string>
|
||||
<string name="backup_and_restore__restore__metadata_error_nothing_to_restore">Ez az archívum nem tartalmaz helyreállítható adatokat, válasszon másikat.</string>
|
||||
<string name="backup_and_restore__restore__mode">Helyreállítási mód</string>
|
||||
<string name="backup_and_restore__restore__mode_merge">Egyesítés a jelenlegi adatokkal</string>
|
||||
<string name="backup_and_restore__restore__mode_erase_and_overwrite">Törlés és a jelenlegi adatok felülírása</string>
|
||||
<string name="backup_and_restore__restore__success">Sikeresen visszaállított adatok!</string>
|
||||
@@ -511,14 +508,14 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="clipboard__disabled__message">A {app_name} vágólapelőzményei lehetővé teszik, gyorsan eltároljon és elérjen szövegeket és képeket másolásra, az elemek kitűzésének, az automatikus törlésnek és a maximum elemszám beállításának lehetőségével.</string>
|
||||
<string name="clipboard__disabled__enable_button">Vágólapelőzmények engedélyezése</string>
|
||||
<string name="clipboard__empty__title">A vágólapja üres</string>
|
||||
<string name="clipboard__empty__message">Ha kimásol szövegeket vagy képeket, itt fognak megjelenni.</string>
|
||||
<string name="clipboard__empty__message">Ha szövegeket vagy képeket másol, itt fognak megjelenni.</string>
|
||||
<string name="clipboard__locked__title">A vágólapja zárolva van</string>
|
||||
<string name="clipboard__locked__message">A vágólapelőzmények eléréséhez, kérjük először oldja fel a készülékét.</string>
|
||||
<string name="clipboard__locked__message">A vágólapelőzmények eléréséhez először oldja fel az eszközt.</string>
|
||||
<string name="clipboard__group_pinned">Kitűzött</string>
|
||||
<string name="clipboard__group_recent">Legutóbbi</string>
|
||||
<string name="clipboard__group_other">Egyéb</string>
|
||||
<string name="clipboard__item_description_email">Email</string>
|
||||
<string name="clipboard__item_description_url">URL</string>
|
||||
<string name="clipboard__item_description_email">E-mail-cím</string>
|
||||
<string name="clipboard__item_description_url">Webcím</string>
|
||||
<string name="clipboard__item_description_phone">Telefon</string>
|
||||
<string name="clip__clear_history">Előzmények törlése</string>
|
||||
<string name="clip__unpin_item">Elem levétele</string>
|
||||
@@ -568,8 +565,8 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="devtools__show_key_touch_boundaries__summary" comment="Summary of Show key touch boundaries in Devtools">A billentyűérintési határok körvonalának vörös kiemelése</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__label" comment="Label of Show drag and drop helpers in Devtools">A húzd és vidd segítség megjelenítése</string>
|
||||
<string name="devtools__show_drag_and_drop_helpers__summary" comment="Summary of Show drag and drop helpers in Devtools">Debug célból az amúgy láthatatlan segítség megjelenítése a húzd is vidd ablakoknál</string>
|
||||
<string name="devtools__clear_udm_internal_database__label" comment="Label of Clear internal user dictionary database in Devtools">Belső felhasználói szótár adatbázis ürítése</string>
|
||||
<string name="devtools__clear_udm_internal_database__summary" comment="Summary of Clear internal user dictionary database in Devtools">Kiürít minden szót a szótár adatbázis táblázatból</string>
|
||||
<string name="devtools__clear_udm_internal_database__label" comment="Label of Clear internal user dictionary database in Devtools">Belső felhasználói szótáradatbázis ürítése</string>
|
||||
<string name="devtools__clear_udm_internal_database__summary" comment="Summary of Clear internal user dictionary database in Devtools">Kiürít minden szót a szótár adatbázistáblájából</string>
|
||||
<string name="devtools__reset_flag__label" comment="Label of Reset flag preferences in Devtools">{flag_name} zászló visszaállítása</string>
|
||||
<string name="devtools__reset_flag_is_ime_set_up__summary" comment="Summary of Reset is IME set up flag in Devtools">Hibakeresési művelet a beállítás varázsló újbóli megjelenítéséhez</string>
|
||||
<string name="devtools__test_crash_report__label" comment="Label of Test Crash Report in Devtools">Összeomlásjelentő képernyő tesztelése</string>
|
||||
@@ -577,13 +574,18 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="devtools__group_android__title" comment="Title of Android group in Devtools">Android rendszereszközök</string>
|
||||
<string name="devtools__android_settings_global__title" comment="Title of Android settings (global) screen">Globális Android beállítások</string>
|
||||
<string name="devtools__android_settings_secure__title" comment="Title of Android settings (secure) screen">Biztonságos Android beállítások</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Rendszer Android beállítások</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Rendszer nyelvek</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Rendszer Android beállításai</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Rendszer nyelvei</string>
|
||||
<string name="devtools__debuglog__title">Hibakeresési napló</string>
|
||||
<string name="devtools__debuglog__copied_to_clipboard">A hibakeresési napló a vágólapra lett másolva</string>
|
||||
<string name="devtools__debuglog__copy_log">Napló másolása</string>
|
||||
<string name="devtools__debuglog__copy_for_github">Napló másolása (GitHub formázás)</string>
|
||||
<string name="devtools__debuglog__loading">Betöltés…</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Kiegészítők</string>
|
||||
<string name="ext__list__ext_theme">Téma kiterjesztések</string>
|
||||
<string name="ext__list__ext_keyboard">Billentyűzet kiterjesztések</string>
|
||||
<string name="ext__list__ext_languagepack">Nyelvi csomag kiterjesztések</string>
|
||||
<string name="ext__home__title">Kiterjesztések</string>
|
||||
<string name="ext__list__ext_theme">Témakiterjesztések</string>
|
||||
<string name="ext__list__ext_keyboard">Billentyűzetkiterjesztések</string>
|
||||
<string name="ext__list__ext_languagepack">Nyelvicsomag-kiterjesztések</string>
|
||||
<string name="ext__meta__authors">Szerzők</string>
|
||||
<string name="ext__meta__components">Csomagolt összetevők</string>
|
||||
<string name="ext__meta__components_theme">Tartalmazott témák</string>
|
||||
@@ -636,31 +638,31 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="ext__import__file_skip_ext_not_supported" comment="Reason string when file is loaded in incorrect context">Egy médiafájlt várt (kép, hang, betűtípus, stb.), de egy kiterjesztésarchívumot kapott.</string>
|
||||
<string name="ext__import__file_skip_media_not_supported" comment="Reason string when file is loaded in incorrect context">Egy kiterjesztésarchívumot várt, de egy médiafájlt (kép, hang, betűtípus, stb.) kapott.</string>
|
||||
<string name="ext__import__error_unexpected_exception" comment="Label when an error occurred during import. The error message will be appended below this text view">Váratlan hiba történt az importálás során. A részletek a következőek:</string>
|
||||
<string name="ext__validation__enter_package_name">Kérjük adjon meg egy csomagnevet</string>
|
||||
<string name="ext__validation__enter_package_name">Adjon meg egy csomagnevet</string>
|
||||
<string name="ext__validation__error_package_name">A csomagnév nem egyezik a reguláris kifejezéssel: {id_regex}</string>
|
||||
<string name="ext__validation__enter_version">Kérjük adjon meg egy verziót</string>
|
||||
<string name="ext__validation__enter_title">Kérjük adjon meg egy címet</string>
|
||||
<string name="ext__validation__enter_maintainer">Kérjük adjon meg legalább egy érvényes karbantartót</string>
|
||||
<string name="ext__validation__enter_license">Kérjük adjon meg egy licencazonosítót</string>
|
||||
<string name="ext__validation__enter_component_id">Kérjük adjon meg egy összetevőazonosítót</string>
|
||||
<string name="ext__validation__error_component_id">Kérjük adjon meg egy összetevőazonosítót a következőhöz: {component_id_regex}</string>
|
||||
<string name="ext__validation__enter_component_label">Kérjük adjon meg egy összetevőcímet</string>
|
||||
<string name="ext__validation__hint_component_label_to_long">Az összetevőcím elég hosszú, ami lehet, hogy le lesz vágva a felületen</string>
|
||||
<string name="ext__validation__error_author">Kérjük adjon meg legalább egy érvényes szerzőt</string>
|
||||
<string name="ext__validation__enter_version">Adjon meg egy verziót</string>
|
||||
<string name="ext__validation__enter_title">Adjon meg egy címet</string>
|
||||
<string name="ext__validation__enter_maintainer">Adjon meg legalább egy érvényes karbantartót</string>
|
||||
<string name="ext__validation__enter_license">Adjon meg egy licencazonosítót</string>
|
||||
<string name="ext__validation__enter_component_id">Adjon meg egy összetevő-azonosítót</string>
|
||||
<string name="ext__validation__error_component_id">Adjon meg egy összetevő-azonosítót a következőhöz: {component_id_regex}</string>
|
||||
<string name="ext__validation__enter_component_label">Adjon meg egy összetevőcímet</string>
|
||||
<string name="ext__validation__hint_component_label_to_long">Az összetevőcím elég hosszú, lehet, hogy le lesz vágva a felületen</string>
|
||||
<string name="ext__validation__error_author">Adjon meg legalább egy érvényes szerzőt</string>
|
||||
<string name="ext__validation__error_stylesheet_path_blank">A stíluslap útvonala nem lehet üres</string>
|
||||
<string name="ext__validation__error_stylesheet_path">Kérjük adjon meg egy érvényes stíluslap útvonalat a következőhöz: {stylesheet_path_regex}</string>
|
||||
<string name="ext__validation__enter_property">Kérjük adjon meg egy változónevet</string>
|
||||
<string name="ext__validation__error_property">Kérjük adjon meg egy változónevet a következőhöz: {variable_name_regex}</string>
|
||||
<string name="ext__validation__error_stylesheet_path">Adjon meg egy érvényes stíluslap-útvonalat a következőhöz: {stylesheet_path_regex}</string>
|
||||
<string name="ext__validation__enter_property">Adjon meg egy változónevet</string>
|
||||
<string name="ext__validation__error_property">Adjon meg egy változónevet a következőhöz: {variable_name_regex}</string>
|
||||
<string name="ext__validation__hint_property" tools:ignore="TypographyDashes">Megállapodás szerint a FlorisCSS változónevek két kötőjellel kezdődnek (--)</string>
|
||||
<string name="ext__validation__enter_color">Kérjük adjon meg egy szín sztringet</string>
|
||||
<string name="ext__validation__error_color">Kérjük adjon meg egy érvényes szín sztringet</string>
|
||||
<string name="ext__validation__enter_dp_size">Kérjük adjon meg egy dp méretet</string>
|
||||
<string name="ext__validation__enter_valid_number">Kérjük adjon meg egy érvényes számot</string>
|
||||
<string name="ext__validation__enter_positive_number">Kérjük adjon meg egy pozitív számot (>=0)</string>
|
||||
<string name="ext__validation__enter_percent_size">Kérjük adjon meg egy százalékot</string>
|
||||
<string name="ext__validation__enter_number_between_0_100">Kérjük adjon meg egy pozitív számot 0 és 100 között</string>
|
||||
<string name="ext__validation__hint_value_above_50_percent">Bármilyen érték 50% felett úgy fog viselkedni, mintha 50%-ra állította volna, fontolja meg hogy csökkenti a százalékot</string>
|
||||
<string name="ext__update_box__internet_permission_hint">Mivel ennek az alkalmazásnak nincs internetelérési útvonala, a telepített kiterjesztések frissítéseit manuálisan kell engedélyezni.</string>
|
||||
<string name="ext__validation__enter_color">Adjon meg egy színkarakterláncot</string>
|
||||
<string name="ext__validation__error_color">Adjon meg egy érvényes színkarakterláncot</string>
|
||||
<string name="ext__validation__enter_dp_size">Adjon meg egy dp méretet</string>
|
||||
<string name="ext__validation__enter_valid_number">Adjon meg egy érvényes számot</string>
|
||||
<string name="ext__validation__enter_positive_number">Adjon meg egy pozitív számot (>=0)</string>
|
||||
<string name="ext__validation__enter_percent_size">Adjon meg egy százalékot</string>
|
||||
<string name="ext__validation__enter_number_between_0_100">Adjon meg egy pozitív számot 0 és 100 között</string>
|
||||
<string name="ext__validation__hint_value_above_50_percent">Bármilyen 50% feletti érték úgy fog viselkedni, mintha 50%-ra állította volna, fontolja meg hogy csökkenti a százalékot</string>
|
||||
<string name="ext__update_box__internet_permission_hint">Mivel ennek az alkalmazásnak nincs internetelérési engedélye, a telepített kiterjesztések frissítéseit kézileg kell ellenőrizni.</string>
|
||||
<string name="ext__update_box__search_for_updates">Frissítések keresése</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">{extensions} kezelése</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">A kiterjesztések importálásával, exportálásával, létrehozásával, testreszabásával és eltávolításával kapcsolatos összes feladat a központi kiterjesztéskezelőn keresztül kezelhető.</string>
|
||||
@@ -669,6 +671,7 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="ext__home__visit_store">Kiegészítőbolt meglátogatása</string>
|
||||
<string name="ext__home__manage_extensions">Telepített kiterjesztések kezelése</string>
|
||||
<string name="ext__list__view_details">Részletek megtekintése</string>
|
||||
<string name="ext__check_updates__title">Frissítések keresése</string>
|
||||
<!-- Action strings -->
|
||||
<string name="action__add">Hozzáadás</string>
|
||||
<string name="action__apply">Alkalmaz</string>
|
||||
@@ -687,7 +690,7 @@ A Florisboard-ban ez csak arra szolgál, hogy összeomlás esetén megnyíljon a
|
||||
<string name="action__import">Importálás</string>
|
||||
<string name="action__no">Nem</string>
|
||||
<string name="action__ok">OK</string>
|
||||
<string name="action__restore">Visszaállítás</string>
|
||||
<string name="action__restore">Helyreállítás</string>
|
||||
<string name="action__save">Mentés</string>
|
||||
<string name="action__select">Kiválasztás</string>
|
||||
<string name="action__select_dir">Könyvtár kiválasztása</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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>
|
||||
<string name="prefs__media__emoji_recently_used_max_size">Ukuran maksimum histori emoji</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Warna kulit emoji yang disukai</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Gaya rambut emoji yang disukai</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Objek</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Simbol</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Bendera</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Tidak ditemukan emoji yang digunakan baru-baru ini. Setelah Anda mulai mengetikkan emoji, mereka akan muncul di sini secara otomatis.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro tip: Tekan lama emoji yang terakhir digunakan untuk menghapusnya dari tampilan ini!</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">Menghapus {emoji} dari emoji terakhir yang digunakan</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Panah atas</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Lakukan panah atas</string>
|
||||
@@ -308,6 +304,7 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="pref__keyboard__long_press_delay__label" comment="Preference title">Tundaan tekan lama tombol</string>
|
||||
<string name="pref__keyboard__space_bar_switches_to_characters__label" comment="Preference title">Bilah spasi beralih ke karakter</string>
|
||||
<string name="pref__keyboard__space_bar_switches_to_characters__summary" comment="Preference summary">Beralih kembali ke karakter saat dalam simbol atau numerik secara otomatis</string>
|
||||
<string name="pref__keyboard__incognito_indicator__label" comment="Preference title">Indikator samaran</string>
|
||||
<!-- Smartbar strings -->
|
||||
<string name="settings__smartbar__title" comment="Title of Smartbar screen">Smartbar</string>
|
||||
<string name="pref__smartbar__enabled__label" comment="Preference title">Aktifkan Smartbar</string>
|
||||
@@ -410,6 +407,7 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="pref__advanced__settings_theme__light" comment="Possible value of Settings theme preference in Advanced">Cerah</string>
|
||||
<string name="pref__advanced__settings_theme__dark" comment="Possible value of Settings theme preference in Advanced">Gelap</string>
|
||||
<string name="pref__advanced__settings_theme__amoled_dark" comment="Possible value of Settings theme preference in Advanced">AMOLED Gelap</string>
|
||||
<string name="pref__advanced__settings_material_you__label" comment="Label of Material You preference in Advanced">Gunakan Material You</string>
|
||||
<string name="pref__advanced__settings_language__label" comment="Label of Settings language preference in Advanced">Bahasa pengaturan</string>
|
||||
<string name="pref__advanced__show_app_icon__label" comment="Label of Show app icon preference in Advanced">Tampilkan ikon aplikasi pada peluncur</string>
|
||||
<string name="pref__advanced__show_app_icon__summary_atleast_q" comment="Summary of Show app icon preference in Advanced for Android 10+">Selalu diaktifkan di Android 10+ karena pembatasan sistem</string>
|
||||
@@ -447,6 +445,12 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="setup__select_ime__title">Pilih {app_name}</string>
|
||||
<string name="setup__select_ime__description">{app_name} sekarang sudah diaktifkan di sistem Anda. Untuk menggunakannya secara aktif, beralihlah ke {app_name} dengan memilihnya di dialog pemilih input!</string>
|
||||
<string name="setup__select_ime__switch_keyboard_btn">Ubah Papan Ketik</string>
|
||||
<string name="setup__grant_notification_permission__title">Izinkan Notifikasi Pelaporan Crash</string>
|
||||
<string name="setup__grant_notification_permission__description">Per-Android 13+, apl harus meminta izin untuk
|
||||
mengirim notifikasi. Pada Florisboard, izin tersebut hanya digunakan untuk membuka jendela pelaporan crash pada saat terjadi crash.
|
||||
Izin ini dapat diubah kapanpun pada setelan sistem.
|
||||
</string>
|
||||
<string name="setup__grant_notification_permission__btn">Izinkan</string>
|
||||
<string name="setup__finish_up__title">Selesaikan</string>
|
||||
<string name="setup__finish_up__description_p1">{app_name} sekarang sudah diaktifkan di sistem ini dan siap untuk disesuaikan oleh Anda.</string>
|
||||
<string name="setup__finish_up__description_p2">Jika Anda mengalami masalah, bug, crash, atau hanya ingin membuat saran, lihat repositori proyek di layar tentang!</string>
|
||||
@@ -463,6 +467,10 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_keyboard">Ekstensi papan ketik</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_spelling">Ekstensi / kamus ejaan</string>
|
||||
<string name="backup_and_restore__back_up__files_ime_theme">Ekstensi tema</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history">Histori papan klip</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_text_items">Item teks</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_image_items">Gambar</string>
|
||||
<string name="backup_and_restore__back_up__files_clipboard_history__clipboard_video_items">Video</string>
|
||||
<string name="backup_and_restore__back_up__success">Berhasil mengekspor arsip cadangan!</string>
|
||||
<string name="backup_and_restore__back_up__failure">Gagal untuk mengekspor arsip cadangan: {error_message}</string>
|
||||
<string name="backup_and_restore__restore__title">Pulihkan data</string>
|
||||
@@ -534,6 +542,11 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="pref__clipboard__max_history_size__label">Batas ukuran riwayat</string>
|
||||
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__label">Membersihkan klip utama memengaruhi riwayat</string>
|
||||
<string name="pref__clipboard__clear_primary_clip_deletes_last_item__summary">Membersihkan klip utama juga menghapus entri riwayat terkini</string>
|
||||
<string name="send_to_clipboard__unknown_error">Terjadi kesalahan yang tidak diketahui. Harap coba lagi!</string>
|
||||
<string name="send_to_clipboard__type_not_supported_error">Jenis media ini tidak didukung.</string>
|
||||
<string name="send_to_clipboard__android_version_to_old_error">Versi android terlalu lawas untuk fitur ini.
|
||||
</string>
|
||||
<string name="send_to_clipboard__description__copied_image_to_clipboard">Salin gambar berikut ke papan klip.</string>
|
||||
<!-- Devtools strings -->
|
||||
<string name="devtools__title" comment="Title of Devtools screen. Translators: treat this string as 'Developer tools' for translation, except a similar short term is available for your language.">Alat pengembang</string>
|
||||
<string name="devtools__enabled__label" comment="Label of Enable developer tools in Devtools">Aktifkan alat pengembang</string>
|
||||
@@ -562,6 +575,10 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="devtools__android_settings_system__title" comment="Title of Android settings (system) screen">Pengaturan sistem Android</string>
|
||||
<string name="devtools__android_locales__title" comment="Title of Android locales screen">Sistem lokal</string>
|
||||
<!-- Extension strings -->
|
||||
<string name="ext__home__title">Addon & Ekstensi</string>
|
||||
<string name="ext__list__ext_theme">Ekstensi tema</string>
|
||||
<string name="ext__list__ext_keyboard">Ekstensi keyboard</string>
|
||||
<string name="ext__list__ext_languagepack">Ekstensi paket bahasa</string>
|
||||
<string name="ext__meta__authors">Pembuat</string>
|
||||
<string name="ext__meta__components">Komponen yang dibundel</string>
|
||||
<string name="ext__meta__components_theme">Tema terbundel</string>
|
||||
@@ -614,6 +631,40 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="ext__import__file_skip_ext_not_supported" comment="Reason string when file is loaded in incorrect context">Diduga sebuah file media (gambar, suara, font, dll.) tetapi menemukan sebuah arsip ekstensi.</string>
|
||||
<string name="ext__import__file_skip_media_not_supported" comment="Reason string when file is loaded in incorrect context">Diduga sebuah arsip ekstensi tetapi menemukan sebuah file media (gambar, suara, font, dll.).</string>
|
||||
<string name="ext__import__error_unexpected_exception" comment="Label when an error occurred during import. The error message will be appended below this text view">Sebuah kesalahan yang tidak terduga terjadi saat mengimpor. Detail berikut ini disediakan:</string>
|
||||
<string name="ext__validation__enter_package_name">Harap masukkan nama paket</string>
|
||||
<string name="ext__validation__error_package_name">Nama paket tidak cocok dengan regex {id_regex}</string>
|
||||
<string name="ext__validation__enter_version">Harap masukkan versi</string>
|
||||
<string name="ext__validation__enter_title">Harap masukkan judul</string>
|
||||
<string name="ext__validation__enter_maintainer">Harap masukkan setidaknya seorang maintener yang valid</string>
|
||||
<string name="ext__validation__enter_license">Harap masukkan lisensi pengidentifikasi</string>
|
||||
<string name="ext__validation__enter_component_id">Harap masukkan ID komponen</string>
|
||||
<string name="ext__validation__error_component_id">Harap masukkan ID komponen yang cocok dengan {component_id_regex}</string>
|
||||
<string name="ext__validation__enter_component_label">Harap masukkan label komponen</string>
|
||||
<string name="ext__validation__hint_component_label_to_long">Label komponen Anda terlalu panjang, yang mungkin akan terlipat pada jendela UI</string>
|
||||
<string name="ext__validation__error_author">Harap masukkan setidaknya seorang pembuat yang valid</string>
|
||||
<string name="ext__validation__error_stylesheet_path_blank">Alur stylesheet tidak boleh kosong</string>
|
||||
<string name="ext__validation__error_stylesheet_path">Harap masukkan alur stylesheet yang valid cocok dengan {stylesheet_path_regex}</string>
|
||||
<string name="ext__validation__enter_property">Harap masukkan nama variabel</string>
|
||||
<string name="ext__validation__error_property">Harap masukkan nama variabel yang valid cocok dengan {variable_name_regex}</string>
|
||||
<string name="ext__validation__hint_property" tools:ignore="TypographyDashes">Berdasarkan konvensi nama variabel FlorisCSS dimulai dengan dua garis strip (--)</string>
|
||||
<string name="ext__validation__enter_color">Harap masukkan string warna</string>
|
||||
<string name="ext__validation__error_color">Harap masukkan string warna yang valid</string>
|
||||
<string name="ext__validation__enter_dp_size">Harap masukkan ukuran dp</string>
|
||||
<string name="ext__validation__enter_valid_number">Harap masukkan angka yang valid</string>
|
||||
<string name="ext__validation__enter_positive_number">Harap masukkan bilangan positif (>=0)</string>
|
||||
<string name="ext__validation__enter_percent_size">Harap masukkan ukuran persen</string>
|
||||
<string name="ext__validation__enter_number_between_0_100">Harap masukkan bilangan positif antara 0 dan 100</string>
|
||||
<string name="ext__validation__hint_value_above_50_percent">Nilai apa pun di atas 50% akan berperilaku seolah-olah Anda menetapkan 50%, pertimbangkan untuk menurunkan ukuran persen Anda</string>
|
||||
<string name="ext__update_box__internet_permission_hint">Karena aplikasi ini tidak memiliki izin Internet, pembaruan untuk ekstensi yang diinstal harus diperiksa secara manual.</string>
|
||||
<string name="ext__update_box__search_for_updates">Cari Pembaruan</string>
|
||||
<string name="ext__addon_management_box__managing_placeholder">Kelola {extensions}</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">Semua tugas yang terkait dengan mengimpor, mengekspor, membuat, menyesuaikan, dan menghapus ekstensi dapat ditangani melalui manajer addon terpusat.</string>
|
||||
<string name="ext__addon_management_box__go_to_page">Ke {ext_home_title}</string>
|
||||
<string name="ext__home__info">Anda dapat mengunduh dan menginstal ekstensi dari FlorisBoard Addons Store atau mengimpor file ekstensi apa pun yang telah Anda unduh dari internet.</string>
|
||||
<string name="ext__home__visit_store">Kunjungi Add-ons Store</string>
|
||||
<string name="ext__home__manage_extensions">Kelola ekstensi terpasang</string>
|
||||
<string name="ext__list__view_details">Lihat rincian</string>
|
||||
<string name="ext__check_updates__title">Periksa Pembaruan</string>
|
||||
<!-- Action strings -->
|
||||
<string name="action__add">Tambahkan</string>
|
||||
<string name="action__apply">Terapkan</string>
|
||||
@@ -708,6 +759,8 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="enum__key_hint_mode__hint_priority__description" comment="Enum value description">Karakter awal yang dipilih setelah ditekan lama selalu merupakan simbol petunjuk, atau aksen utama jika tidak ada simbol petunjuk yang tersedia</string>
|
||||
<string name="enum__key_hint_mode__smart_priority" comment="Enum value label">Prioritas pintar</string>
|
||||
<string name="enum__key_hint_mode__smart_priority__description" comment="Enum value description">Karakter awal yang dipilih setelah ditekan lama secara dinamis akan memutuskan untuk menjadi aksen utama atau simbol petunjuk, berdasarkan bahasa dan tata letak saat ini</string>
|
||||
<string name="enum__incognito_display_mode__replace_shared_actions_toggle" comment="Enum value label">Ganti ikon sakelar tindakan bersama dengan indikator penyamaran</string>
|
||||
<string name="enum__incognito_display_mode__display_behind_keyboard" comment="Enum value label">Tampilkan indikator penyamaran di belakang keyboard</string>
|
||||
<string name="enum__incognito_mode__force_off" comment="Enum value label">Matikan secara paksa</string>
|
||||
<string name="enum__incognito_mode__force_off__description" comment="Enum value description">Mode samaran akan selalu dinonaktifkan, tidak tergantung pada opsi dari aplikasi. Tindakan cepat samaran dalam Smartbar tidak akan selalu tersedia dengan opsi ini.</string>
|
||||
<string name="enum__incognito_mode__force_on" comment="Enum value label">Nyalakan secara paksa</string>
|
||||
@@ -742,23 +795,23 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="enum__smartbar_layout__suggestions_actions_extended__description" comment="Enum value description">Baris dan baris tindakan yang dapat dialih tambahan statis, dengan tindakan menempel</string>
|
||||
<string name="enum__snygg_level__basic" comment="Enum value label">Dasar</string>
|
||||
<string name="enum__snygg_level__basic__description" comment="Enum value description">Hanya properti warna yang ditampilkan, properti dan aturan diterjemahkan.</string>
|
||||
<string name="enum__snygg_level__advanced" comment="Enum value label">Tingkat lanjut</string>
|
||||
<string name="enum__snygg_level__advanced__description" comment="Enum value description">Semua properti warna yang ditampilkan, properti dan aturan diterjemahkan.</string>
|
||||
<string name="enum__snygg_level__advanced" comment="Enum value label">Lanjutan</string>
|
||||
<string name="enum__snygg_level__advanced__description" comment="Enum value description">Semua properti ditampilkan, properti dan aturan diterjemahkan.</string>
|
||||
<string name="enum__snygg_level__developer" comment="Enum value label">Pengembang</string>
|
||||
<string name="enum__snygg_level__developer__description" comment="Enum value description">Semua properti ditampilkan, properti dan aturan ditampilkan seperti yang tertulis di file lembar gayanya.</string>
|
||||
<string name="enum__snygg_level__developer__description" comment="Enum value description">Semua properti ditampilkan, properti dan aturan ditampilkan seperti yang tertulis pada file stylesheet tersebut.</string>
|
||||
<string name="enum__space_bar_mode__nothing" comment="Enum value label">Tidak ada label</string>
|
||||
<string name="enum__space_bar_mode__current_language" comment="Enum value label">Bahasa saat ini</string>
|
||||
<string name="enum__space_bar_mode__space_bar_key" comment="Enum value label">␣</string>
|
||||
<string name="enum__spelling_language_mode__use_system_languages" comment="Enum value label">Gunakan bahasa sistem</string>
|
||||
<string name="enum__spelling_language_mode__use_keyboard_subtypes" comment="Enum value label">Gunakan subtipe papan ketik</string>
|
||||
<string name="enum__spelling_language_mode__use_keyboard_subtypes" comment="Enum value label">Gunakan subtipe keyboard</string>
|
||||
<string name="enum__swipe_action__no_action" comment="Enum value label">Tidak ada aksi</string>
|
||||
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Ubah ke mode papan ketik sebelumnya</string>
|
||||
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Ubah ke mode papan ketik selanjutnya</string>
|
||||
<string name="enum__swipe_action__cycle_to_previous_keyboard_mode" comment="Enum value label">Ubah ke mode keyboard sebelumnya</string>
|
||||
<string name="enum__swipe_action__cycle_to_next_keyboard_mode" comment="Enum value label">Ubah ke mode keyboard selanjutnya</string>
|
||||
<string name="enum__swipe_action__delete_character" comment="Enum value label">Hapus karakter sebelum kursor</string>
|
||||
<string name="enum__swipe_action__delete_characters_precisely" comment="Enum value label">Hapus karakter secara tepat</string>
|
||||
<string name="enum__swipe_action__delete_word" comment="Enum value label">Hapus kata sebelum kursor</string>
|
||||
<string name="enum__swipe_action__delete_words_precisely" comment="Enum value label">Hapus kata secara tepat</string>
|
||||
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">Sembunyikan papan ketik</string>
|
||||
<string name="enum__swipe_action__hide_keyboard" comment="Enum value label">Sembunyikan keyboard</string>
|
||||
<string name="enum__swipe_action__insert_space" comment="Enum value label">Masukkan spasi</string>
|
||||
<string name="enum__swipe_action__move_cursor_up" comment="Enum value label">Pindahkan kursor ke atas</string>
|
||||
<string name="enum__swipe_action__move_cursor_down" comment="Enum value label">Pindahkan kursor ke bawah</string>
|
||||
@@ -768,7 +821,7 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="enum__swipe_action__move_cursor_end_of_line" comment="Enum value label">Pindahkan kursor ke akhir baris</string>
|
||||
<string name="enum__swipe_action__move_cursor_start_of_page" comment="Enum value label">Pindahkan kursor ke awal halaman</string>
|
||||
<string name="enum__swipe_action__move_cursor_end_of_page" comment="Enum value label">Pindahkan kursor ke akhir halaman</string>
|
||||
<string name="enum__swipe_action__switch_to_clipboard_context" comment="Enum value label">Buka pengelola/riwayat papan klip</string>
|
||||
<string name="enum__swipe_action__switch_to_clipboard_context" comment="Enum value label">Buka manager/histori papan klip</string>
|
||||
<string name="enum__swipe_action__shift" comment="Enum value label">Shift</string>
|
||||
<string name="enum__swipe_action__redo" comment="Enum value label">Ulangi</string>
|
||||
<string name="enum__swipe_action__undo" comment="Enum value label">Urungkan</string>
|
||||
@@ -779,13 +832,13 @@ Klik di sini untuk menyelesaikan masalah ini.</string>
|
||||
<string name="enum__swipe_action__switch_to_prev_subtype" comment="Enum value label">Beralih ke subtipe sebelumnya</string>
|
||||
<string name="enum__swipe_action__switch_to_next_subtype" comment="Enum value label">Beralih ke subtipe selanjutnya</string>
|
||||
<string name="enum__swipe_action__toggle_smartbar_visibility" comment="Enum value label">Alih visibilitas Smartbar</string>
|
||||
<string name="enum__theme_mode__always_day" comment="Enum value label">Selalu pagi</string>
|
||||
<string name="enum__theme_mode__always_day" comment="Enum value label">Selalu siang</string>
|
||||
<string name="enum__theme_mode__always_night" comment="Enum value label">Selalu malam</string>
|
||||
<string name="enum__theme_mode__follow_system" comment="Enum value label">Ikuti sistem</string>
|
||||
<string name="enum__theme_mode__follow_time" comment="Enum value label">Ikuti waktu</string>
|
||||
<string name="enum__utility_key_action__switch_to_emojis" comment="Enum value label">Beralih ke emoji</string>
|
||||
<string name="enum__utility_key_action__switch_language" comment="Enum value label">Beralih bahasa</string>
|
||||
<string name="enum__utility_key_action__switch_keyboard_app" comment="Enum value label">Beralih aplikasi papan ketik</string>
|
||||
<string name="enum__utility_key_action__switch_keyboard_app" comment="Enum value label">Beralih aplikasi keyboard</string>
|
||||
<string name="enum__utility_key_action__dynamic_switch_language_emojis" comment="Enum value label">Dinamis: Beralih ke emoji / Beralih bahasa</string>
|
||||
<!-- Unit strings (symbols) -->
|
||||
<!-- Unit strings (written words) -->
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
<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">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">Dimensione massima della cronologia degli emoji</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Carnagione emoji preferita</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Acconciatura emoji preferita</string>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">Emoji usate</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">Abilita storico Emoji</string>
|
||||
<string name="prefs__media__emoji_history_enabled__summary" comment="Preference summary">Conserva le emoji usate recentemente per un accesso rapido</string>
|
||||
<string name="prefs__media__emoji_history_pinned_update_strategy" comment="Preference title">Pianifica aggiornamento (selezionato)</string>
|
||||
<string name="prefs__media__emoji_history_recent_update_strategy" comment="Preference title">Pianifica aggiornamento (recente)</string>
|
||||
<string name="prefs__media__emoji_history_max_size">Numero massimo di elementi da tenere</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">Faccine & Emoticons</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">Persone & Corpo</string>
|
||||
@@ -26,10 +31,12 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Oggetti</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Simboli</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Bandiere</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Non è stata trovata nessuna emoji usata di recente. Una volta che inizi a digitare le emoji appariranno automaticamente qui.</string>
|
||||
<string name="emoji__recently_used__phone_locked_message" comment="Message to show if phone is locked">Per accedere alla cronologia delle emoji, sbloccare prima il dispositivo.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Consiglio: premi a lungo le emoji usate di recente per rimuoverle di nuovo da questa 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">{emoji} rimosso dagli emoji usati di recente</string>
|
||||
<string name="emoji__history__empty_message" comment="Message if the emoji history is empty">Nessuna emoji usata recentemente è stata trovata. Quando inizierai a scrivere appariranno qui.</string>
|
||||
<string name="emoji__history__phone_locked_message" comment="Message to show if phone is locked">Per accedere allo storico emoji, prima sblocca il dispositivo.</string>
|
||||
<string name="emoji__history__usage_tip" comment="Feature discoverability for actions of emoji history">Suggerimento: Tieni premuto a lungo sulle emoji nello storico per selezionarle o rimuoverle!</string>
|
||||
<string name="emoji__history__removal_success_message" comment="Toast message if user has used the delete action on an emoji in the emoji history">{emoji} rimossa dallo storico</string>
|
||||
<string name="emoji__history__pinned">Selezionata</string>
|
||||
<string name="emoji__history__recent">Recente</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">Freccia su</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Eseguire freccia su</string>
|
||||
@@ -612,6 +619,7 @@
|
||||
<string name="ext__validation__enter_version">Inserisci una versione</string>
|
||||
<string name="ext__validation__enter_title">Inserisci un titolo</string>
|
||||
<string name="ext__validation__enter_maintainer">Inserisci un indirzzo e-mail valido</string>
|
||||
<string name="ext__validation__enter_license">Inserisci un ID per la licenza</string>
|
||||
<string name="ext__validation__enter_component_id">Inserisci l\'ID corretto</string>
|
||||
<string name="ext__addon_management_box__addon_manager_info">Tutte le attività relative all\'importazione, all\'esportazione, alla creazione, alla personalizzazione e alla rimozione delle estensioni possono essere gestite tramite il gestore interno</string>
|
||||
<string name="ext__home__info">Puoi scaricare e installare estensioni dallo Store di FlorisBoard o importare qualsiasi addon</string>
|
||||
@@ -685,6 +693,14 @@
|
||||
<string name="enum__display_language_names_in__system_locale__description" comment="Enum value description">I nomi delle lingue nell\'app e nell\'interfaccia utente della tastiera vengono visualizzati nella lingua che è impostata per tutto il dispositivo</string>
|
||||
<string name="enum__display_language_names_in__native_locale" comment="Enum value label">Lingua originaria</string>
|
||||
<string name="enum__display_language_names_in__native_locale__description" comment="Enum value description">I nomi delle lingue nell\'app e nell\'interfaccia utente della tastiera vengono visualizzati nella lingua a cui fanno riferimento</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend" comment="Enum value label">Ordinamento automatico (anteponi)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_prepend__description" comment="Enum value description">Ordina automaticamente le emoji in base al loro utilizzo. Le nuove emoji verranno aggiunte all\'inizio.</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append" comment="Enum value label">Ordinamento automatico (aggiungi)</string>
|
||||
<string name="enum__emoji_history_update_strategy__auto_sort_append__description" comment="Enum value description">Ordina automaticamente le emoji in base al loro utilizzo. Le nuove emoji verranno aggiunte alla fine.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend" comment="Enum value label">Ordinamento manuale (anteponi)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_prepend__description" comment="Enum value description">Non ordinare automaticamente le emoji. Le nuove emoji saranno aggiunte all\'inizio.</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append" comment="Enum value label">Ordinamento manuale (aggiungi)</string>
|
||||
<string name="enum__emoji_history_update_strategy__manual_sort_append__description" comment="Enum value description">Non ordinare automaticamente le emoji. Le nuove emoji saranno aggiunte alla fine.</string>
|
||||
<string name="enum__emoji_skin_tone__default" comment="Enum value label">{emoji} Carnagione predefinita</string>
|
||||
<string name="enum__emoji_skin_tone__light_skin_tone" comment="Enum value label">{emoji} Carnagione chiara</string>
|
||||
<string name="enum__emoji_skin_tone__medium_light_skin_tone" comment="Enum value label">{emoji} Carnagione medio-chiara</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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 -->
|
||||
@@ -26,10 +25,6 @@
|
||||
<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__phone_locked_message" comment="Message to show if phone is locked">כדי לגשת להיסטוריית האמוג׳י שלך, קודם יש לפתוח את נעילת המכשיר.</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>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">חץ למעלה</string>
|
||||
<string name="quick_action__arrow_up__tooltip">ביצוע חץ למעלה</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<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 -->
|
||||
@@ -26,13 +25,13 @@
|
||||
<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>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">上矢印</string>
|
||||
<string name="quick_action__arrow_up__tooltip">Perform arrow up</string>
|
||||
<string name="quick_action__arrow_down" maxLength="12">下矢印</string>
|
||||
<string name="quick_action__arrow_down__tooltip">Perform arrow up</string>
|
||||
<string name="quick_action__arrow_left" maxLength="12">左矢印</string>
|
||||
<string name="quick_action__arrow_left__tooltip"></string>
|
||||
<string name="quick_action__arrow_right" maxLength="12">右矢印</string>
|
||||
<string name="quick_action__clipboard_copy" maxLength="12">コピー</string>
|
||||
<string name="quick_action__clipboard_cut" maxLength="12">カット</string>
|
||||
|
||||
@@ -13,9 +13,14 @@
|
||||
<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>
|
||||
<string name="prefs__media__emoji_history__title" comment="Preference group title">이모지 기록</string>
|
||||
<string name="prefs__media__emoji_history_enabled" comment="Preference title">이모지 기록 사용</string>
|
||||
<string name="prefs__media__emoji_suggestion__title" comment="Preference group title">이모지 제안</string>
|
||||
<string name="prefs__media__emoji_suggestion_enabled" comment="Preference title">이모지 제안 사용</string>
|
||||
<string name="prefs__media__emoji_suggestion_update_history" comment="Preference title">이모지 기록 업데이트</string>
|
||||
<string name="prefs__media__emoji_suggestion_candidate_show_name" comment="Preference title">이모지 이름 표시</string>
|
||||
<!-- Emoji strings -->
|
||||
<string name="emoji__category__smileys_emotion" comment="Emoji category name">스마일리 및 이모티콘</string>
|
||||
<string name="emoji__category__people_body" comment="Emoji category name">사람 및 신체</string>
|
||||
@@ -26,9 +31,7 @@
|
||||
<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>
|
||||
<string name="emoji__history__recent">최근</string>
|
||||
<!-- Quick action strings -->
|
||||
<string name="quick_action__arrow_up" maxLength="12">화살표 위로</string>
|
||||
<string name="quick_action__arrow_up__tooltip">위쪽 화살표 동작 실행</string>
|
||||
@@ -122,6 +125,8 @@
|
||||
<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__localization__subtype_delete_confirmation_title" comment="Title of the subtype delete confirmation dialog">삭제 확인</string>
|
||||
<string name="settings__localization__subtype_delete_confirmation_warning" comment="Warning message in the confirmation dialog to confirm the user's intent to delete">정말 이 하위 유형을 삭제하시겠습니까?</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>
|
||||
@@ -246,20 +251,26 @@
|
||||
<string name="pref__correction__remember_caps_lock_state__label" comment="Preference title">캡스락 상태 기억</string>
|
||||
<string name="pref__correction__remember_caps_lock_state__summary" comment="Preference summary">다른 텍스트 필드로 이동할 때 캡스락 상태 유지</string>
|
||||
<string name="pref__correction__double_space_period__label" comment="Preference title">스페이스 두 번으로 마침표 입력</string>
|
||||
<string name="pref__correction__double_space_period__summary" comment="Preference summary">스페이스 바를 두 번 눌러 마침표를 입력합니다.</string>
|
||||
<string name="pref__correction__double_space_period__summary" comment="Preference summary">스페이스 바를 두 번 눌러 마침표 입력</string>
|
||||
<string name="pref__spelling__title" comment="Preference group title">맞춤법 검사</string>
|
||||
<string name="pref__spelling__active_spellchecker__summary_none">설정한 맞춤법 검사기 서비스가 없습니다. 여기를 눌러 서비스를 설정하세요.</string>
|
||||
<string name="pref__spelling__language_mode__label" comment="Label of Language mode pref">언어</string>
|
||||
<string name="pref__spelling__use_contacts__label" comment="Label of Use contact list pref">연락처 이름 사용</string>
|
||||
<string name="pref__spelling__use_contacts__summary" comment="Summary of Use contact list pref">연락처 목록에서 이름을 찾아 표시합니다.</string>
|
||||
<string name="pref__spelling__use_contacts__summary" comment="Summary of Use contact list pref">연락처 목록에서 이름을 찾아 표시</string>
|
||||
<string name="pref__spelling__use_udm_entries__label" comment="Label of Use user dictionary entries pref">사용자 사전 사용</string>
|
||||
<string name="pref__spelling__use_udm_entries__summary" comment="Summary of Use user dictionary entries pref">사용자 사전에서 항목을 찾아 표시합니다.</string>
|
||||
<string name="pref__spelling__use_udm_entries__summary" comment="Summary of Use user dictionary entries pref">사용자 사전에서 항목을 찾아 표시</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__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__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__open_system_manager_ui" comment="Label of the Open system manager UI menu option">시스템 관리자 UI 열기</string>
|
||||
<string name="settings__udm__dictionary_import_success" comment="Message for dictionary import success">사용자 사전을 잘 가져왔습니다!</string>
|
||||
<string name="settings__udm__dictionary_export_success" comment="Message for dictionary export success">사용자 사전을 잘 내보냈습니다!</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="media__tab__emojis" comment="Tab description for emojis in the media UI">Emojîyan</string>
|
||||
<string name="media__tab__emoticons" comment="Tab description for emoticons in the media UI">Hestnîşan</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">Mezinahiya dîrokê emûcîyan</string>
|
||||
<string name="prefs__media__emoji_preferred_skin_tone">Tona çermê emojî ya tercîhkirî</string>
|
||||
<string name="prefs__media__emoji_preferred_hair_style">Tercîha porê emojî</string>
|
||||
<!-- Emoji strings -->
|
||||
@@ -26,9 +25,6 @@
|
||||
<string name="emoji__category__objects" comment="Emoji category name">Amanc</string>
|
||||
<string name="emoji__category__symbols" comment="Emoji category name">Sembol</string>
|
||||
<string name="emoji__category__flags" comment="Emoji category name">Alayen</string>
|
||||
<string name="emoji__recently_used__empty_message" comment="Message if no recently used emojis exist">Emojîyên ku di demên dawî da hatine bikarînan nehatine dîtin. Piştî ku te dest bi tîpkirina emojîyan kir, dê li vir bi awayekî otomatîk xuya bibin.</string>
|
||||
<string name="emoji__recently_used__removal_tip" comment="Feature discoverability for removal of recently used emojis">Pro tip: Çapemeniya dirêj di demên dawî de emojî bikar anîn da ku wan ji vê nêrînê dîsa rakin!</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">Rakirin {emoji} ji emojiyên ku di demên dawî de hatine bikaranîn</string>
|
||||
<!-- Quick action strings -->
|
||||
<!-- Incognito mode strings -->
|
||||
<!-- Settings UI strings -->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user