diff --git a/.github/workflows/post-pr.yml b/.github/workflows/post-pr.yml
index 5cde95e625..bf948064ed 100644
--- a/.github/workflows/post-pr.yml
+++ b/.github/workflows/post-pr.yml
@@ -31,7 +31,7 @@ jobs:
ui-tests:
name: UI Tests (Synapse)
needs: should-i-run
- runs-on: macos-latest
+ runs-on: buildjet-4vcpu-ubuntu-2204
strategy:
fail-fast: false
matrix:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index cd7e26f3cf..fb8e3080ae 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -13,7 +13,10 @@ env:
jobs:
tests:
name: Runs all tests
- runs-on: macos-latest # for the emulator
+ runs-on: buildjet-4vcpu-ubuntu-2204
+ strategy:
+ matrix:
+ api-level: [28]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
@@ -36,40 +39,70 @@ jobs:
httpPort: 8080
disableRateLimiting: true
public_baseurl: "http://10.0.2.2:8080/"
+
+ - name: AVD cache
+ uses: actions/cache@v3
+ id: avd-cache
+ with:
+ path: |
+ ~/.android/avd/*
+ ~/.android/adb*
+ key: avd-${{ matrix.api-level }}
+
+ - name: create AVD and generate snapshot for caching
+ if: steps.avd-cache.outputs.cache-hit != 'true'
+ uses: reactivecircus/android-emulator-runner@v2
+ with:
+ api-level: ${{ matrix.api-level }}
+ arch: x86
+ profile: Nexus 5X
+ force-avd-creation: true # Is set to false in the doc https://github.com/ReactiveCircus/android-emulator-runner
+ emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
+ disable-animations: true
+ script: echo "Generated AVD snapshot for caching."
+
- name: Run all the codecoverage tests at once
- id: tests
uses: reactivecircus/android-emulator-runner@v2
- continue-on-error: true
+ # continue-on-error: true
with:
- api-level: 28
+ api-level: ${{ matrix.api-level }}
arch: x86
profile: Nexus 5X
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
- emulator-build: 7425822
+ # emulator-build: 7425822
script: |
./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
- # NB: continue-on-error marks steps.tests.conclusion = 'success' but leaves stes.tests.outcome = 'failure'
- - name: Run all the codecoverage tests at once (retry if emulator failed)
- uses: reactivecircus/android-emulator-runner@v2
- if: always() && steps.tests.outcome == 'failure' # don't run if previous step succeeded.
+ # NB: continue-on-error marks steps.tests.conclusion = 'success' but leaves steps.tests.outcome = 'failure'
+ ### - name: Run all the codecoverage tests at once (retry if emulator failed)
+ ### uses: reactivecircus/android-emulator-runner@v2
+ ### if: always() && steps.tests.outcome == 'failure' # don't run if previous step succeeded.
+ ### with:
+ ### api-level: 28
+ ### arch: x86
+ ### profile: Nexus 5X
+ ### force-avd-creation: false
+ ### emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
+ ### disable-animations: true
+ ### emulator-build: 7425822
+ ### script: |
+ ### ./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
+ ### ./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
+ ### ./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
+ ### ./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
+
+ - name: Upload Integration Test Report Log
+ uses: actions/upload-artifact@v3
+ if: always()
with:
- api-level: 28
- arch: x86
- profile: Nexus 5X
- force-avd-creation: false
- emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
- disable-animations: true
- emulator-build: 7425822
- script: |
- ./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
- ./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
- ./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
- ./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
+ name: integration-test-error-results
+ path: |
+ */build/outputs/androidTest-results/connected/
+ */build/reports/androidTests/connected/
# we may have failed a previous step and retried, that's OK
- name: Publish results to Sonar
diff --git a/CHANGES.md b/CHANGES.md
index 518bbd8b67..f0ecb5fc04 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,41 @@
+Changes in Element v1.5.0 (2022-09-21)
+======================================
+
+Features ✨
+----------
+ - Deferred DMs - Enable and move the feature to labs settings ([#7180](https://github.com/vector-im/element-android/issues/7180))
+
+Bugfixes 🐛
+----------
+ - Fix text margin in QR code view when no display name is set ([#5424](https://github.com/vector-im/element-android/issues/5424))
+ - [App Layout] Recents carousel now scrolled to first position when new item added to or moved to this position ([#6776](https://github.com/vector-im/element-android/issues/6776))
+ - Fixed problem when room list's scroll did jump after rooms placeholders were replaced with rooms summary items ([#7079](https://github.com/vector-im/element-android/issues/7079))
+ - Fixes crash when quickly double clicking FABs in the new app layout ([#7102](https://github.com/vector-im/element-android/issues/7102))
+ - Fixes space list and new chat bottom sheets showing too small in New App Layout (especially evident in landscape) ([#7103](https://github.com/vector-im/element-android/issues/7103))
+ - [App Layout] Room leaving prompt dialog now waits user to confirm leaving before do so ([#7122](https://github.com/vector-im/element-android/issues/7122))
+ - Fix empty verification bottom sheet. ([#7130](https://github.com/vector-im/element-android/issues/7130))
+ - [New Layout] Fixes new chat dialog not getting dismissed after selecting its actions ([#7132](https://github.com/vector-im/element-android/issues/7132))
+ - Fixes Room List not getting updated when fragment is not in focus ([#7186](https://github.com/vector-im/element-android/issues/7186))
+
+In development 🚧
+----------------
+ - Create DM room only on first message - Add a spinner when sending the first message ([#6970](https://github.com/vector-im/element-android/issues/6970))
+ - [Device Manager] Filter Other Sessions ([#7045](https://github.com/vector-im/element-android/issues/7045))
+ - [Device management] Session details screen ([#7077](https://github.com/vector-im/element-android/issues/7077))
+ - Create DM room only on first message - Fix glitch in the room list ([#7121](https://github.com/vector-im/element-android/issues/7121))
+ - Create DM room only on first message - Handle the local rooms within the new AppLayout ([#7153](https://github.com/vector-im/element-android/issues/7153))
+
+Other changes
+-------------
+ - [Modules] Lifts the application variants to the app module ([#6779](https://github.com/vector-im/element-android/issues/6779))
+ - Ensure that we do not expect all the Event fields when requesting `rooms/{roomId}/hierarchy` endpoint. ([#7035](https://github.com/vector-im/element-android/issues/7035))
+ - Move some GitHub actions to buildjet runners, and remove the second attempt to run integration tests. ([#7108](https://github.com/vector-im/element-android/issues/7108))
+ - Exclude legacy android support annotation library ([#7140](https://github.com/vector-im/element-android/issues/7140))
+ - Pulling no longer hosted im.dlg:android-dialer directly into the repository and removing legacy support library usages ([#7142](https://github.com/vector-im/element-android/issues/7142))
+ - Fixing build cache misses when compiling the vector module ([#7157](https://github.com/vector-im/element-android/issues/7157))
+ - New App Layout is now enabled by default! Go to the Settings > Labs to toggle this ([#7166](https://github.com/vector-im/element-android/issues/7166))
+
+
Changes in Element v1.4.36 (2022-09-10)
=======================================
diff --git a/build.gradle b/build.gradle
index a40790d441..49dc1e7fb4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ buildscript {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
classpath "com.likethesalad.android:stem-plugin:2.2.2"
- classpath 'org.owasp:dependency-check-gradle:7.1.2'
+ classpath 'org.owasp:dependency-check-gradle:7.2.0'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
diff --git a/coverage.gradle b/coverage.gradle
index 716f9b7cc7..2c0af25368 100644
--- a/coverage.gradle
+++ b/coverage.gradle
@@ -81,11 +81,11 @@ task generateCoverageReport(type: JacocoReport) {
task unitTestsWithCoverage(type: GradleBuild) {
// the 7.1.3 android gradle plugin has a bug where enableTestCoverage generates invalid coverage
startParameter.projectProperties.coverage = [enableTestCoverage: false]
- tasks = [':vector:testGplayDebugUnitTest', ':matrix-sdk-android:testDebugUnitTest']
+ tasks = ['testDebugUnitTest']
}
task instrumentationTestsWithCoverage(type: GradleBuild) {
startParameter.projectProperties.coverage = [enableTestCoverage: true]
startParameter.projectProperties['android.testInstrumentationRunnerArguments.notPackage'] = 'im.vector.app.ui'
- tasks = [':vector-app:connectedGplayDebugAndroidTest', ':vector:connectedGplayDebugAndroidTest', 'matrix-sdk-android:connectedDebugAndroidTest']
+ tasks = [':vector-app:connectedGplayDebugAndroidTest', ':vector:connectedDebugAndroidTest', 'matrix-sdk-android:connectedDebugAndroidTest']
}
diff --git a/dependencies.gradle b/dependencies.gradle
index 3759763fb7..9641a63f26 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -15,14 +15,14 @@ def gradle = "7.1.3"
def kotlin = "1.6.21"
def kotlinCoroutines = "1.6.4"
def dagger = "2.42"
-def appDistribution = "16.0.0-beta03"
+def appDistribution = "16.0.0-beta04"
def retrofit = "2.9.0"
def arrow = "0.8.2"
def markwon = "4.6.2"
def moshi = "1.13.0"
def lifecycle = "2.5.1"
def flowBinding = "1.2.0"
-def flipper = "0.163.0"
+def flipper = "0.164.0"
def epoxy = "4.6.2"
def mavericks = "2.7.0"
def glide = "4.13.2"
@@ -86,7 +86,7 @@ ext.libs = [
'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
'appdistribution' : "com.google.firebase:firebase-appdistribution:$appDistribution",
// Phone number https://github.com/google/libphonenumber
- 'phonenumber' : "com.googlecode.libphonenumber:libphonenumber:8.12.54"
+ 'phonenumber' : "com.googlecode.libphonenumber:libphonenumber:8.12.55"
],
dagger : [
'dagger' : "com.google.dagger:dagger:$dagger",
diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle
index bcd737acc9..433bc53568 100644
--- a/dependencies_groups.gradle
+++ b/dependencies_groups.gradle
@@ -69,8 +69,6 @@ ext.groups = [
'com.gabrielittner.threetenbp',
'com.getkeepsafe.relinker',
'com.github.bumptech.glide',
- 'com.github.filippudak',
- 'com.github.filippudak.progresspieview',
'com.github.javaparser',
'com.github.piasy',
'com.github.shyiko.klob',
diff --git a/fastlane/metadata/android/en-US/changelogs/40105000.txt b/fastlane/metadata/android/en-US/changelogs/40105000.txt
new file mode 100644
index 0000000000..1bfa2b3dea
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Main changes in this version: New App Layout and Deferred DM enabled by default.
+Full changelog: https://github.com/vector-im/element-android/releases
\ No newline at end of file
diff --git a/fastlane/metadata/android/lt/full_description.txt b/fastlane/metadata/android/lt/full_description.txt
new file mode 100644
index 0000000000..7189008456
--- /dev/null
+++ b/fastlane/metadata/android/lt/full_description.txt
@@ -0,0 +1,42 @@
+Element yra ir saugaus žinučių siuntimo, ir produktyvaus komandinio bendradarbiavimo programėlė, puikiai tinkanti grupiniams pokalbiams dirbant nuotoliniu būdu. Ši pokalbių programa naudoja visapusį šifravimą, kad užtikrintų galingas vaizdo konferencijas, dalijimąsi failais ir balso skambučius.
+
+Element funkcijos turi:
+- Išplėstinės bendravimo internetu priemonės
+- Visiškai užšifruotos žinutės, kad būtų galima saugiau bendrauti su įmone, net ir su nuotoliniais darbuotojais
+- Decentralizuoti pokalbiai, pagrįsti atvirojo kodo sistema Matrix
+- Saugus dalijimasis failais su šifruotais duomenimis valdant projektus
+- Vaizdo pokalbiai su IP balso perdavimu ir ekrano bendrinimu
+- Lengva integracija su mėgstamiausiomis internetinėmis bendradarbiavimo priemonėmis, projektų valdymo įrankiais, VoIP paslaugomis ir kitomis komandinių pokalbių programomis
+
+Element visiškai skiriasi nuo kitų žinučių siuntimo ir bendradarbiavimo programėlių. Ji veikia Matrix - atvirame tinkle, skirtame saugiam žinučių siuntimui ir decentralizuotam bendravimui. Jame galima savarankiškai talpinti duomenis ir žinutes savo serveryje, kad naudotojai galėtų maksimaliai valdyti ir kontroliuoti savo duomenis ir žinutes.
+
+Privatumas ir šifruotos žinutės
+Element apsaugo jus nuo nepageidaujamų reklamų, duomenų gavybos ir uždarų sodų. Jis taip pat apsaugo visus jūsų duomenis, "vienas su vienu" vaizdo ir balso ryšį, naudodamas visapusį šifravimą ir kryžmiškai pasirašytą įrenginių patvirtinimą.
+
+Element suteikia galimybę kontroliuoti savo privatumą ir kartu saugiai bendrauti su visais, esančiais Matrix tinkle, arba kitais verslo bendradarbiavimo įrankiais integruojantis su tokiomis programėlėmis kaip Slack.
+
+Element gali būti savarankiškai talpinamas
+Kad galėtumėte geriau kontroliuoti savo slaptus duomenis ir pokalbius, Element gali būti savarankiškai talpinamas arba galite pasirinkti bet kurį Matrix pagrindu veikiantį serverį - atvirojo kodo decentralizuoto bendravimo standartu. Element suteikia privatumą, saugumo atititikimą ir integracijos lankstumą.
+
+Jūsų duomenys priklauso jums
+Jūs nusprendžiate, kur laikyti savo duomenis ir žinutes. Be duomenų gavybos ar trečiųjų šalių prieigos rizikos.
+
+Element suteikia jums kontrolę įvairiais būdais:
+1. Gaukite nemokamą paskyrą viešajame serveryje matrix.org, kurį talpina Matrix kūrėjai, arba rinkitės iš tūkstančių viešųjų serverių, kurių talpinimą teikia savanoriai
+2. Savarankiškai talpinkite savo paskyrą, naudodami serverį savo IT infrastruktūroje
+3. Užsisakykite paskyrą nuosavame serveryje tiesiog užsisakydami "Element Matrix Services" talpinimo paslaugą
+
+Atviras žinučių siuntimas ir bendradarbiavimas
+Galite bendrauti su bet kuriuo Matrix tinklo nariu, nesvarbu, ar jis naudojasi Element, kita Matrix programėle, ar net jei naudoja kitą žinučių siuntimo programėlę.
+
+Super saugus
+Tikras visapusis šifravimas (žinutes gali iššifruoti tik pokalbio dalyviai) ir kryžminiu parašu patvirtintas įrenginių patvirtinimas.
+
+Pilnas bendravimas ir integracija
+Žinučių siuntimas, balso ir vaizdo skambučiai, failų ir ekrano bendrinimas ir daugybė integracijų, robotų ir valdiklių. Kurkite kambarius, bendruomenes, palaikykite ryšį ir atlikite darbus.
+
+Tęskite darbą ten, kur baigėte
+Palaikykite ryšį, kad ir kur būtumėte, naudodami visiškai sinchronizuotą žinučių istoriją visuose įrenginiuose ir internete adresu https://app.element.io
+
+Atviras kodas
+Element Android yra atvirojo kodo projektas, kurį talpina GitHub. Praneškite apie klaidas ir (arba) prisidėkite prie jo kūrimo adresu https://github.com/vector-im/element-android
diff --git a/fastlane/metadata/android/lt/short_description.txt b/fastlane/metadata/android/lt/short_description.txt
new file mode 100644
index 0000000000..600e76b35d
--- /dev/null
+++ b/fastlane/metadata/android/lt/short_description.txt
@@ -0,0 +1 @@
+Grupiniai pokalbiai - šifruotos žinutės ir vaizdo skambučiai
diff --git a/fastlane/metadata/android/lt/title.txt b/fastlane/metadata/android/lt/title.txt
new file mode 100644
index 0000000000..d911c34bb2
--- /dev/null
+++ b/fastlane/metadata/android/lt/title.txt
@@ -0,0 +1 @@
+Element - Saugūs pokalbiai
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104180.txt b/fastlane/metadata/android/nl-NL/changelogs/40104180.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104180.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104190.txt b/fastlane/metadata/android/nl-NL/changelogs/40104190.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104190.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104200.txt b/fastlane/metadata/android/nl-NL/changelogs/40104200.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104200.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104220.txt b/fastlane/metadata/android/nl-NL/changelogs/40104220.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104220.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104230.txt b/fastlane/metadata/android/nl-NL/changelogs/40104230.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104230.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104240.txt b/fastlane/metadata/android/nl-NL/changelogs/40104240.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104240.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104250.txt b/fastlane/metadata/android/nl-NL/changelogs/40104250.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104250.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104270.txt b/fastlane/metadata/android/nl-NL/changelogs/40104270.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104270.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104280.txt b/fastlane/metadata/android/nl-NL/changelogs/40104280.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104280.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104320.txt b/fastlane/metadata/android/nl-NL/changelogs/40104320.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104320.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/nl-NL/changelogs/40104340.txt b/fastlane/metadata/android/nl-NL/changelogs/40104340.txt
new file mode 100644
index 0000000000..48796d85bc
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/changelogs/40104340.txt
@@ -0,0 +1,2 @@
+Belangrijkste veranderingen in deze versie: Verscheidene foutoplossingen en stabiliteitsverbeteringen.
+Volledige lijst met veranderingen: https://github.com/vector-im/element-android/releases
diff --git a/gradle.properties b/gradle.properties
index 2af9214ed5..0e561faa8d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,6 +12,7 @@ org.gradle.jvmargs=-Xmx4g -Xms512M -XX:MaxPermSize=2048m -XX:MaxMetaspaceSize=1g
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.vfs.watch=true
+org.gradle.caching=true
# Android Settings
android.enableJetifier=true
diff --git a/library/jsonviewer/build.gradle b/library/jsonviewer/build.gradle
index ad472b0b54..fcad3f1087 100644
--- a/library/jsonviewer/build.gradle
+++ b/library/jsonviewer/build.gradle
@@ -55,8 +55,9 @@ dependencies {
implementation libs.airbnb.mavericks
// Span utils
- implementation 'me.gujun.android:span:1.7'
-
+ implementation('me.gujun.android:span:1.7') {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ }
implementation libs.jetbrains.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid
diff --git a/library/ui-strings/build.gradle b/library/ui-strings/build.gradle
index 860fc3c980..6a31f24c9b 100644
--- a/library/ui-strings/build.gradle
+++ b/library/ui-strings/build.gradle
@@ -20,3 +20,7 @@ android {
jvmTarget = "11"
}
}
+
+tasks.withType( com.likethesalad.android.templates.common.tasks.BaseTask) {
+ it.outputs.cacheIf { true }
+}
diff --git a/library/ui-strings/src/main/res/values-ar/strings.xml b/library/ui-strings/src/main/res/values-ar/strings.xml
index 073f961cb6..70b9a33ab5 100644
--- a/library/ui-strings/src/main/res/values-ar/strings.xml
+++ b/library/ui-strings/src/main/res/values-ar/strings.xml
@@ -320,7 +320,7 @@
السمةخطأ في فكّ التعميةاسم الجهاز
- معرّف الجهاز
+ معرّف الجهازمفتاح الجهازصدّر مفاتيح الغرفةصدّر المفاتيح إلى ملف محلي
diff --git a/library/ui-strings/src/main/res/values-bg/strings.xml b/library/ui-strings/src/main/res/values-bg/strings.xml
index b29823040f..d3e9e599bc 100644
--- a/library/ui-strings/src/main/res/values-bg/strings.xml
+++ b/library/ui-strings/src/main/res/values-bg/strings.xml
@@ -396,7 +396,7 @@
ТемаГрешка при разшифрованеПублично име
- Сесийно ID
+ Сесийно IDКлюч на устройствоЕкспортирай E2E ключове за стаяЕкспортиране на ключове за стая
diff --git a/library/ui-strings/src/main/res/values-bn-rBD/strings.xml b/library/ui-strings/src/main/res/values-bn-rBD/strings.xml
index 2f068f1bf8..7897da934e 100644
--- a/library/ui-strings/src/main/res/values-bn-rBD/strings.xml
+++ b/library/ui-strings/src/main/res/values-bn-rBD/strings.xml
@@ -789,7 +789,7 @@
রুমের কুঞ্জিগুলি এক্সপোর্ট করুনশেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুনসেশানের কুঞ্জি
- আইডি
+ আইডিসর্বজনীন নামডিক্রিপশন সমস্যাথিম
diff --git a/library/ui-strings/src/main/res/values-bn-rIN/strings.xml b/library/ui-strings/src/main/res/values-bn-rIN/strings.xml
index 828bc3bd34..56bde36977 100644
--- a/library/ui-strings/src/main/res/values-bn-rIN/strings.xml
+++ b/library/ui-strings/src/main/res/values-bn-rIN/strings.xml
@@ -693,7 +693,7 @@
ডিক্রিপশন সমস্যাসর্বজনীন নাম
- আইডি
+ আইডিসেশানের কুঞ্জিশেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুন
diff --git a/library/ui-strings/src/main/res/values-ca/strings.xml b/library/ui-strings/src/main/res/values-ca/strings.xml
index 13a5b6c119..863fa13fbb 100644
--- a/library/ui-strings/src/main/res/values-ca/strings.xml
+++ b/library/ui-strings/src/main/res/values-ca/strings.xml
@@ -448,7 +448,7 @@
TemaError al desxifrarNom públic
- ID de sessió
+ ID de sessióClau de sessióExporta les claus de la sala E2EExporta les claus de la sala
@@ -1470,7 +1470,7 @@
%d sessió activa%d sessions actives
- Aquesta sessió és de confiança per a xats segurs ja que l\'has verificada tu:
+ Aquesta sessió és de confiança per a missatges segurs ja que l\'has verificada tu:Desconnecta aquesta sessióGestió de sessionsVeure totes les sessions
@@ -1844,7 +1844,7 @@
Altres idiomes disponiblesIdioma actualMotiu de l\'eliminació
- Aquesta sessió és de confiança per a xats segurs ja que %1$s (%2$s) l\'ha verificat:
+ Aquesta sessió és de confiança per a missatges segurs ja que %1$s (%2$s) l\'ha verificat:Obtenint clau de corbaNo s\'ha pogut crear el xat. Comprova els usuaris que vols convidar i torna-ho a provar.Verifica manualment mitjançant text
@@ -2225,7 +2225,7 @@
Tria on es desen les teves converses, et dona control i independència. Connectat a través de Matrix.Comunicació segura i independent que t\'ofereix el mateix nivell de privadesa que una conversa cara a cara a casa teva.Missatgeria pel teu equip.
- Missatgeria segura.
+ Missatges segurs.Ets propietari de les teves converses.Tu tens el control.Trucada finalitzada • %1$s
@@ -2602,8 +2602,8 @@
Tots els xatsPreferències de dissenyExplora sales
- Per estar més segur, verifica les teves sessions i tanca qualsevol sessió que no reconeguis o ja no utilitzis.
- Altres sessions
+ Per estar més segur, verifica les teves sessions i tanca qualsevol sessió que no reconeguis o ja no utilitzis.
+ Altres sessionsSessionsObre la llista d\'espaisCrea un nou xat o sala
@@ -2619,14 +2619,11 @@
Mostra totes les sessions (V2, WIP)Crea salaInicia xat
- Verifica la teva sessió actual per a missatges segurs millorats.Verificada · Última activitat %1$sNo verificada · Última activitat %1$sVeure-ho tot (%1$d)
- Sessió actualVeure detallsVerifica sessió
- La sessió actual està llesta per la missatgeria segura.Sessió no verificadaSessió verificadaTipus de dispositiu desconegut
@@ -2636,4 +2633,45 @@
Aquesta sala no s\'ha trobat.
\nTorna-ho a provar més tard.%sInvitacions
-
+ ${app_name}
+\nHola, %s.
+ Nova visualització!
+ Prova-ho
+ Entra a espais
+ L\'aplicació de xats segurs tot en un. Per a equips, amics i organitzacions. Crea un xat o uneix-te a una sala existent per començar.
+
+ Pensa en tancar sessió de les sessions antigues (%1$d dia o més) que ja no utilitzis.
+ Pensa en tancar sessió de les sessions antigues (%1$d dies o més) que ja no utilitzis.
+
+ Prem la part superior dreta per veure l\'opció d\'enviar comentaris.
+ Envia comentaris
+ Aquí es mostraran els teus missatges no llegits, quan en tinguis.
+ Sense novetats.
+ Verifica les sessions o tanca\'n la sessió si no estan verificades.
+ Per simplificar ${app_name}, les pestanyes ara son opcionals. Gestiona-les mitjançant el menú de la part superior dreta.
+ %s
+\nsembla una mica buit.
+ Sessions inactives
+ Sessions no verificades
+ Millora la seguretat del teu compte seguint aquestes recomanacions.
+ Recomanacions de seguretat
+
+ Actiu fa %1$d dia (%2$s)
+ Actiu fa més de %1$d dies (%2$s)
+
+ Aquí és on apareixeran les teves sol·licituds i invitacions.
+ Res de nou.
+ Accedeix als teus espais (part inferior dreta) més ràpid i fàcilment.
+ Els espais són una nova manera d\'agrupar sales i gent. Afegeix una sala o crea\'n una de nova mitjançant el botó de la part inferior dreta.
+ Els espais són una nova manera d\'agrupar sales i gent. Crea\'n un per començar.
+ Cap espai, encara.
+ Amaga els continguts de %s
+ Mostra el contingut de %s
+ Canvia espai
+ Verifica les teves sessions per obtenir missatges segurs millorats o tanca les sessions que no reconeguis o ja no utilitzis.
+ No llest per a missatges segurs
+ Llest per a missatges segurs
+ Aquesta sessió està llesta per a missatges segurs.
+ La teva sessió actual està llesta per a missatges segurs.
+ Verifica la teva sessió actual obtenir missatges segurs millorats.
+
\ No newline at end of file
diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index b7bfeac444..79f8311159 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -635,7 +635,7 @@
Motiv vzhleduChyba dešifrováníVeřejné jméno
- ID relace
+ ID relaceKlíč relaceExport E2E klíčů místnostíExport klíčů místností
@@ -2651,8 +2651,8 @@
Otevřít nastaveníVšechny konverzaceZobrazit všechny relace (V2, WIP)
- V zájmu co nejlepšího zabezpečení ověřujte své relace a odhlašujte se ze všech relací, které již nepoznáváte nebo nepoužíváte.
- Ostatní relace
+ V zájmu co nejlepšího zabezpečení ověřujte své relace a odhlašujte se ze všech relací, které již nepoznáváte nebo nepoužíváte.
+ Ostatní relaceRelaceSeznam otevřených prostorůVytvořit novou konverzaci nebo místnost
@@ -2672,11 +2672,8 @@
Neověřeno · Poslední aktivita %1$sOvěřeno · Poslední aktivita %1$sZobrazit všechny (%1$d)
- Aktuální relaceZobrazit podrobnostiOvěřit relaci
- Ověřte svou aktuální relaci pro vylepšené zabezpečené zasílání zpráv.
- Vaše aktuální relace je připravena pro bezpečné zasílání zpráv.Neověřená relaceOvěřená relaceNeznámý typ zařízení
@@ -2686,4 +2683,41 @@
Je nám líto, tato místnost nebyla nalezena.
\nZkuste to prosím později.%sPozvánky
+ Vyzkoušejte to
+ Klepnutím vpravo nahoře zobrazíte možnost zpětné vazby.
+ Poskytněte zpětnou vazbu
+ Přístup k vašim prostorům (vpravo dole) je rychlejší a snazší než kdykoli předtím.
+ Přístup do prostorů
+ Pro zjednodušení aplikace ${app_name} jsou nyní karty nepovinné. Spravujte je pomocí nabídky vpravo nahoře.
+ Vítejte v novém zobrazení!
+ Zde se zobrazí nepřečtené zprávy, pokud nějaké máte.
+ Nic k nahlášení.
+ Univerzální zabezpečená chatovací aplikace pro týmy, přátele a organizace. Vytvořte si chat nebo se připojte k existující místnosti a začněte.
+ Vítejte v aplikaci ${app_name},
+\n%s.
+ Prostory představují nový způsob seskupování místností a osob. Pomocí tlačítka vpravo dole můžete přidat stávající místnost nebo vytvořit novou.
+ %s
+\nvypadá trochu prázdně.
+
+ Zvažte odhlášení ze starých relací (%1$d den nebo více), které již nepoužíváte.
+ Zvažte odhlášení ze starých relací (%1$d dny nebo více), které již nepoužíváte.
+ Zvažte odhlášení ze starých relací (%1$d dnů nebo více), které již nepoužíváte.
+
+ Neaktivní relace
+ Ověřte nebo se odhlaste z neověřených relací.
+ Neověřené relace
+ Zlepšete zabezpečení svého účtu dodržováním těchto doporučení.
+ Bezpečnostní doporučení
+
+ Neaktivní po dobu %1$d+ dne (%2$s)
+ Neaktivní po dobu %1$d+ dnů (%2$s)
+ Neaktivní po dobu %1$d+ dnů (%2$s)
+
+ Zde se budou nacházet vaše nové žádosti a pozvánky.
+ Nic nového.
+ Prostory představují nový způsob seskupování místností a osob. Vytvořte si prostor a začněte.
+ Zatím žádné prostory.
+ Sbalit podprostory %s
+ Rozbalit podprostory %s
+ Změnit prostor
diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 8e502a6392..e01fc898a3 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -20,7 +20,7 @@
%s hat einen Sprachanruf getätigt.%s hat den Anruf angenommen.%s hat den Anruf beendet.
- %1$s hat den zukünftigen Chatverlauf sichtbar gemacht für %2$s
+ %1$s hat den zukünftigen Nachrichtenverlauf sichtbar gemacht für %2$salle Mitglieder, ab Einladung.alle Mitglieder, ab Beitritt.alle Mitglieder.
@@ -265,11 +265,11 @@
RäumeLogdateien übermittelnAbsturzberichte übermitteln
- Screenshot übermitteln
+ Bildschirmfoto übermittelnProblem meldenBitte beschreibe das Problem. Was hast du genau gemacht\? Was sollte passieren\? Was ist tatsächlich passiert\?Problembeschreibung
- Um Probleme diagnostizieren zu können, werden Protokolle des Clients zusammen mit dem Fehlerbericht übermittelt. Dieser Fehlerbericht wird, wie die Protokolle und der Screenshot, nicht öffentlich sichtbar sein. Wenn du nur den oben eingegebenen Text senden möchtest, die nachfolgenden Haken entsprechend entfernen:
+ Um Probleme diagnostizieren zu können, werden Protokolle des Clients zusammen mit dem Fehlerbericht übermittelt. Dieser Fehlerbericht wird, wie die Protokolle und das Bildschirmfoto, nicht öffentlich sichtbar sein. Wenn du nur den oben eingegebenen Text senden möchtest, die nachfolgenden Haken entsprechend entfernen:Du scheinst dein Telefon frustriert zu schütteln. Möchtest du das Fenster zum Senden eines Fehlerberichts öffnen\?Dein Fehlerbericht wurde erfolgreich übermitteltDer Fehlerbericht konnte nicht übermittelt werden (%s)
@@ -418,7 +418,7 @@
Als Hauptadresse aufhebenEntschlüsselungsfehlerÖffentlicher Name
- Sitzungs-ID
+ Sitzungs-IDSitzungsschlüsselEnde-zu-Ende-Raumschlüssel exportierenRaumschlüssel exportieren
@@ -908,7 +908,7 @@
(bearbeitet)NachrichtenbearbeitungKeine Änderungen gefunden
- Gespräche filtern…
+ Konversationen filtern …Sende eine neue DirektnachrichtDas Raumverzeichnis anzeigenLink in die Zwischenablage kopiert
@@ -994,7 +994,7 @@
Eine Textnachricht wurde an %s gesendet. Bitte gib den Verifizierungscode ein, den sie enthält.Aktiviere ausführliche Logs.Ausführliche Logs werden der Entwicklung der App dadurch helfen, dass mehr Informationen übertragen werden, wenn du einen Fehlerbericht sendest. Auch wenn dies aktiviert ist, werden keine Nachrichteninhalte oder andere privaten Daten aufgezeichnet.
- Bitte erneut versuchen, nachdem du die Nutzungsbedingungen deines Homeservers akzeptiert hast.
+ Bitte erneut versuchen, nachdem du die Nutzungsbedingungen deines Heimservers akzeptiert hast.Bei Benutzung könnten Cookies gesetzt werden und es könnten Daten mit %s geteilt werden:Bei Benutzung könnten Daten mit %s geteilt werden:Optionen zum Finden werden erscheinen, sobald du eine Telefonnummer hinzugefügt hast.
@@ -1409,8 +1409,8 @@
nutze deinen Schlüsselbackup-WiederherstellungsschlüsselWenn du dein Schlüsselbackup-Passwort nicht weißt, kannst du %s.Schlüsselbackup-Wiederherstellungsschlüssel
- Screenshots innerhalb der Anwendung verhindern
- Das Aktivieren dieser Einstellung setzt das FLAG_SECURE in allen Aktivitäten. Starte die Anwendung neu, damit die Änderung wirksam wird.
+ Bildschirmfotos der Anwendung verhindern
+ Das Aktivieren dieser Einstellung setzt FLAG_SECURE in allen Aktivitäten. Starte die Anwendung neu, damit die Änderung wirksam wird.Neues Benutzerpasswort festlegen…Nutze die neueste Version von ${app_name} auf deinen anderen Geräten, ${app_name} Web, ${app_name} Desktop, ${app_name} iOS, ${app_name} für Android oder einen anderen cross-signing-fähigen Matrix-Client${app_name} Web
@@ -1461,7 +1461,7 @@
AblehnenErfolgEchtzeitverbindung konnte nicht hergestellt werden.
-\nBitte den Administrator deines Homeservers, einen TURN-Server zu konfigurieren, dass Anrufe zuverlässig funktionieren.
+\nBitte den Administrator deines Heimservers, einen TURN-Server zu konfigurieren, damit Anrufe zuverlässig funktionieren.Audiogerät auswählenTelefonLautsprecher
@@ -1744,13 +1744,13 @@
DirektnachrichtVerlauf der Anfragen von Schlüsselfreigaben sendenKeine weiteren Ergebnisse
- Starte die Diskussion
+ Beginne ein GesprächAutorisierenMeine Zustimmung widerrufenDu hast zugestimmt E-Mails und Telefonnummern an diesen Identitätsserver zu senden, um von anderen Nutzern entdeckt zu werden.E-Mails und Telefonnummern sendenVorschläge
- Bekannte Nutzer
+ Bekannte PersonenQR-CodeHinzufügen via QR-CodeGib die Erlaubnis, um auf die Kamera zu zugreifen.
@@ -1794,7 +1794,7 @@
Manche Zeichen sind nicht zulässigBitte gib eine Raumadresse anDiese Adresse ist bereits vergeben
- Aktivieren, wenn der Raum nur von Mitgliedern deines Homeservers zur internen Kommunikation verwendet wird. Das kann später nicht mehr geändert werden.
+ Aktivieren, wenn der Raum nur von Mitgliedern deines Heimservers zur internen Kommunikation verwendet wird. Das kann später nicht mehr geändert werden.Begrenze Zugang zu diesem Raum (für immer!) auf Mitglieder von %s%1$d von %2$dKeine Vorschau für diesen Raum verfügbar. Willst du direkt beitreten\?
@@ -1845,7 +1845,7 @@
EinmalanmeldungAnmelden mit %sRegistrieren mit %s
- Mit %s weitermachen
+ Weiter mit %sKnopf zum Nachrichteneditor hinzufügen, der die Emoji-Tastatur öffnetEmoji-Tastatur anzeigenNutze /confetti oder sende Nachrichten mit ❄️ oder 🎉
@@ -2188,7 +2188,7 @@
Hilfreiche Informationen zur Fehlersuche anzeigenDebug-Info anzeigenDas schaut nicht nach einer gültigen E-Mail-Adresse aus
- Nach Name, ID oder E-Mail suchen
+ Mittels Name, ID oder E-Mail-Adresse suchenNeuen Space erstellenZugriffWer hat Zugriff\?
@@ -2587,8 +2587,8 @@
PersonenSchreibe deine erste Nachricht, um %s zur Konversation einzuladenAlle Sitzungen anzeigen (V2, in Arbeit)
- Für bestmögliche Sicherheit verifiziere deine Sitzungen und melde dich von allen ab, die du nicht erkennst oder nutzt.
- Andere Sitzungen
+ Für bestmögliche Sicherheit verifiziere deine Sitzungen und melde dich von allen ab, die du nicht erkennst oder nutzt.
+ Andere SitzungenSitzungenSpace-Liste öffnenBeginne ein Gespräch oder erstelle einen Raum
@@ -2620,6 +2620,23 @@
\nBitte versuche es später erneut.%s
EinladungenNicht verifiziert · Letzte Aktivität %1$s
- Verifiziere deine aktuelle Sitzung für besonders sichere Nachrichtenübertragung.Nicht verifizierte Sitzung
-
\ No newline at end of file
+ Nicht verifizierte Sitzung
+ Verbessere deine Kontosicherheit, indem du diese Empfehlungen beherzigst.
+ Sicherheitsempfehlungen
+
+ Inaktiv seit %1$d+ Tag (%2$s)
+ Inaktiv seit %1$d+ Tagen (%2$s)
+
+ Verifiziert · Letzte Aktivität %1$s
+ Verifizierte Sitzung
+ Unbekannter Gerätetyp
+ Nichts Neues.
+ Spaces sind eine neue Art, Räume und Personen zu organisieren. Erstelle einen Space, um zu beginnen.
+ Noch keine Spaces.
+ Hier werden deine ungelesenen Nachrichten erscheinen, wenn du welche hast.
+ Es gibt nichts Neues.
+ Alle Unterhaltungen
+ Space wechseln
+ Unterhaltung beginnen
+
diff --git a/library/ui-strings/src/main/res/values-el/strings.xml b/library/ui-strings/src/main/res/values-el/strings.xml
index 092a01bff4..f4973f4b95 100644
--- a/library/ui-strings/src/main/res/values-el/strings.xml
+++ b/library/ui-strings/src/main/res/values-el/strings.xml
@@ -172,7 +172,7 @@
ΘέμαΣφάλμα αποκρυπτογράφησηςΌνομα συσκευής
- Αναγνωριστικό συσκευής
+ Αναγνωριστικό συσκευήςΕξαγωγήΕισαγωγήΕπιλέξτε ένα ευρετήριο δωματίων
diff --git a/library/ui-strings/src/main/res/values-eo/strings.xml b/library/ui-strings/src/main/res/values-eo/strings.xml
index 7e1925f708..f536ca00f9 100644
--- a/library/ui-strings/src/main/res/values-eo/strings.xml
+++ b/library/ui-strings/src/main/res/values-eo/strings.xml
@@ -1084,7 +1084,7 @@
Elporti ŝlosilojn de ĉambrojElporti tutvoje ĉifrajn ŝlosilojn de ĉambrojŜlosilo de salutaĵo
- Identigilo de salutaĵo
+ Identigilo de salutaĵoPublika nomoEraris malĉifradoHaŭto
diff --git a/library/ui-strings/src/main/res/values-es-rMX/strings.xml b/library/ui-strings/src/main/res/values-es-rMX/strings.xml
index 0b38fa6a19..c82f9aff61 100644
--- a/library/ui-strings/src/main/res/values-es-rMX/strings.xml
+++ b/library/ui-strings/src/main/res/values-es-rMX/strings.xml
@@ -249,7 +249,7 @@
Desescojer como Dirección PrincipalError en descifrarNombre del dispositivo
- Identificación del dispositivo
+ Identificación del dispositivoClave del dispositivoExportar claves de cifrado de extremo-a-extremo de salasExportar claves de salas
diff --git a/library/ui-strings/src/main/res/values-es/strings.xml b/library/ui-strings/src/main/res/values-es/strings.xml
index 4eec90fbd6..bc4299c1bd 100644
--- a/library/ui-strings/src/main/res/values-es/strings.xml
+++ b/library/ui-strings/src/main/res/values-es/strings.xml
@@ -415,7 +415,7 @@
Dejar de Establecer como dirección principalError de descifradoNombre público
- ID de sesión
+ ID de sesiónClave de sesiónExportar claves de salas con cifrado Extremo-a-ExtremoExportar claves de sala
@@ -2518,4 +2518,136 @@
El destino se ha registrado de forma satisfactoria al servidor doméstico.Registración de punto finalSiguiente
+ Pruébalo
+ Danos tu opinión
+ Acceder a espacios
+ Para simplificar ${app_name}, las pestañas son opcionales. Gestiónalas usando el menú en la esquina superior derecha.
+ ¡Bienvenido a una nueva interfaz!
+ Nada que reportar.
+ Bienvenido a ${app_name},
+\n%s.
+ %s
+\nparece un poco vacío.
+ Sesiones inactivas
+ Verifica o cierra sesión de sesiones sin verificar.
+ Sesiones sin verificar
+ Mejora la seguridad de tu cuenta siguiendo estas recomendaciones.
+ Consejos de seguridad
+
+ Inactiva por %1$d+ día (%2$s)
+ Inactiva por %1$d+ días (%2$s)
+
+ Sin verificar · Última actividad %1$s
+ Verificada · Última actividad %1$s
+ Ver todos (%1$d)
+ Ver detalles
+ Verificar sesión
+ Sesión sin verificar
+ Sesión verificada
+ Tipo de dispositivo desconocido
+ Escritorio
+ Web
+ Móvil
+ Mostrar todas las sesiones (V2, WIP)
+ Auto aprovar widgets de Element Call y dar permisos de cámara y micrófono
+
+ %d mensaje borrado
+ %d mensajes borrados
+
+ Ubicación en tiempo real
+ Compartir ubicación
+ Debes tener el permiso correspondiente para compartir ubicaciones en esta sala.
+ No tienes permiso para compartir ubicaciones
+ No se pudo cargar el mapa
+\nEste servidor doméstico puede que no esté configurado para mostrar mapas.
+ Los resultados podrán verse cuando la encuesta termine
+ MSC3061: Compartir claves de sala para mensajes anteriores
+ Abrir ajustes
+ Envía tu primer mensaje para invitar a %s
+ Los mensajes en esta sala están encriptados de extremo a extremo.
+ Este código QR parece incorrecto. Por favor, intente verificar con otro método.
+ No serás capaz de acceder al historial de mensajes encriptado. Restablece tu backup de mensajes seguro y las claves de verificación para empezar de cero.
+ No se ha podido verificar el dispositivo
+ Para más seguridad, verifica tus sesiones y cierra cualquiera que no reconozcas o hayas dejado de usar.
+ Otras sesiones
+ Sesiones
+ No se puede abrir este enlace: las comunidades han sido reemplazadas por espacios
+ Usuario / Email / Teléfono
+ ¿Eres una persona\?
+ Sigue las instrucciones enviadas a %s
+ Restablecer contraseña
+ Olvidé mi contraseña
+ Volver a enviar correo
+ ¿No recibiste ningún email\?
+ Sigue las instrucciones enviadas a %s
+ Verifica tu email
+ Volver a enviar código
+ Código enviado a %s
+ Confirma tu número de teléfono
+ Cerrar sesión en todos los dispositivos
+ Restablecer contraseña
+ Asegúrate de que tiene al menos 8 caracteres.
+ Elige una nueva contraseña
+ Nueva contraseña
+ Comprueba tu email.
+ %s te enviará un enlace de verificación
+ Código de confirmación
+ Número de teléfono
+ %s necesita verificar tu cuenta
+ Escribe tu número de teléfono
+ Email
+ %s necesita verificar tu cuenta
+ Introduce tu email
+ Por favor, lee las condiciones de uso de %s
+ Políticas del servidor
+ Ponte en contacto
+ ¿Deseas hospedar tu propio servidor\?
+ URL del servidor
+ ¿Cuál es la dirección de tu servidor\?
+ ¿Cuál es la dirección de tu servidor\? Será donde se guarden todos tus datos
+ Selecciona un servidor
+ ¡Hola de nuevo!
+ Editar
+ O
+ Dónde se guardarán tus conversaciones
+ Dónde se guardarán tus conversaciones
+ Debe tener al menos 8 caracteres
+ Otros pueden buscarte como %s
+ Crea tu cuenta
+ Abrir lista de espacios
+ Crear una nueva conversación o sala
+ Ir
+ Actualizando tus datos…
+ Personas
+ Favoritos
+ Sin leer
+ Todo
+ Lo sentimos, esta sala no se ha encontrado.
+\nPor favor, inténtelo de nuevo.%s
+ Usar ajustes por defecto del sistema
+ Escoger manualmente
+ Tamaño automático de fuente
+ Escoger tamaño de la fuente
+
+ %1$s y %2$d otro
+ %1$s y %2$d otros
+
+ %1$s y %2$s
+ Email no verificado, comprueba tu bandeja de entrada
+ Aquí es donde tus nuevas solicitudes y invitaciones estarán.
+ Nada nuevo.
+ Invitaciones
+ Los espacios son una nueva forma de agrupar salas y personas. Crea un espacio para empezar.
+ No hay espacios aún.
+ A - Z
+ Actividad
+ Ordenar por
+ Mostrar recientes
+ Mostrar filtros
+ Ajustes de disposición
+ Explorar salas
+ Cambiar espacio
+ Crear sala
+ Iniciar conversación
+ Todas las conversaciones
diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml
index 55fb9dfef0..9bd1dd23b7 100644
--- a/library/ui-strings/src/main/res/values-et/strings.xml
+++ b/library/ui-strings/src/main/res/values-et/strings.xml
@@ -612,7 +612,7 @@
Need on alles katsejärgus olevad funktsionaalsused. Ole kasutamisel ettevaatlik.Dekrüptimise vigaAvalik nimi
- Sessiooni tunnus
+ Sessiooni tunnusSessiooni võtiEkspordi jututubade läbiva krüptimise võtmedEkspordi jututoa võtmed
@@ -2592,8 +2592,8 @@
Ava seadistusedKõik vestlusedNäita kõiki sessioone (V2, WIP)
- Parima turvalisuse nimel verifitseeri kõik oma sessioonid ning logi välja neist, mida sa enam ei kasuta.
- Muud sessioonid
+ Parima turvalisuse nimel verifitseeri kõik oma sessioonid ning logi välja neist, mida sa enam ei kasuta.
+ Muud sessioonidSessionidAva kogukondade loendAlusta uut vestlust või loo uus jututuba
@@ -2613,11 +2613,8 @@
Verifitseerimata · Viimati kasutusel %1$sVerifitseeritud · Viimati kasutusel %1$sNäita kõiki (%1$d)
- Praegune sessioonVaata lisateavetVerifitseeri sessioon
- Turvalise sõnumivahetuse nimel palun verifitseeri oma praegune sessioon.
- Sinu praegune sessioon on valmis turvaliseks sõnumivahetuseks.Verifitseerimata sessioonVerifitseeritud sessioonTundmatu seadme tüüp
@@ -2627,4 +2624,39 @@
Vabandust, aga seda jututuba ei õnnestu leida.
\nPalun proovi hiljem uuesti.%sKutsed
+ Uut teavet ei leidu.
+ Kogukonnad on viis jututubade ja inimeste ühendamiseks. Alustamiseks võid luua uue kogukonna.
+ Siin veel pole kogukondi.
+ Vaheta kogukonda
+ Proovi nüüd
+ Tagasiside valikute nägemiseks klõpsi ülal paremal.
+ Jaga tagasisidet
+ Kogukonnad leiad alt paremalt kiiremini ja lihtsamini, kui varem.
+ Ligipääs kogukondadele
+ Et ${app_name}\'i kasutamine oleks lihtsam, siis kaardid on nüüd valikulised. Neid saad hallata ülal paremal avanevast menüüst.
+ Meie liidesel on nüüd uus vaade!
+ Kui sul on lugemata sõnumeid, siis nad on siit leitavad.
+ Hetkel siin polegi midagi põnevat.
+ Paljude võimalustega turvaline suhtlusrakendus sõprade, kogukondade ja tiimide jaoks. Alustamiseks loo mõni uus vestlus või liitu olemasoleva jututoaga.
+ %s,
+\ntere tulemast ${app_name} kasutajaks.
+ Kogukonnad on võimalus jututubade ja inimeste ühendamiseks. Kasutades all paremal olevat nuppu lisa mõni olemasolev jututuba või loo uus.
+ %s
+\ntundub olema tühjavõitu.
+
+ Logi välja sellisest vanast sessioonist (vanem kui %1$d päev), mida sa enam ei kasuta.
+ Logi välja sellistest vanadest sessioonidest (vanemad kui %1$d päeva), mida sa enam ei kasuta.
+
+ Mitteaktiivsed sessioonid
+ Logi verifitseerimata sessioonidest välja või verifitseeri nad.
+ Verifitseerimata sessioonid
+ Kui järgid neid soovitusi, siis sa parandad oma kasutajakonto turvalisust.
+ Turvalisusega seotud soovitused
+
+ Pole olnud kasutusel %1$d+ päeva (%2$s)
+ Pole olnud kasutusel %1$d+ päeva (%2$s)
+
+ Siin saavad olema sinu tulevased päringud ja kutsed.
+ Ahenda %s alamkogukonnad
+ Näita %s alamkogukondi
diff --git a/library/ui-strings/src/main/res/values-eu/strings.xml b/library/ui-strings/src/main/res/values-eu/strings.xml
index 7b27d1cc1d..f1f834ee04 100644
--- a/library/ui-strings/src/main/res/values-eu/strings.xml
+++ b/library/ui-strings/src/main/res/values-eu/strings.xml
@@ -406,7 +406,7 @@ Kontuan izan ekintza honek aplikazioa berrabiaraziko duela eta denbora bat behar
Deszifratze erroreaIzen publikoa
- IDa
+ IDaSaioaren gakoaEsportatu E2E geletako gakoak
diff --git a/library/ui-strings/src/main/res/values-fa/strings.xml b/library/ui-strings/src/main/res/values-fa/strings.xml
index e104225389..400a8121f9 100644
--- a/library/ui-strings/src/main/res/values-fa/strings.xml
+++ b/library/ui-strings/src/main/res/values-fa/strings.xml
@@ -678,7 +678,7 @@
اینها ویژگیهای آزمایشیای هستند که ممکن است به روشهای نامنتظرهای حراب شوندا. با احتیاط استفاده کنید.تنظیم به عنوان نشانی اصلینام عمومی
- شناسهٔ نشست
+ شناسهٔ نشستکلید نشستبرونریزی کلیدهای اتاقهای سرتاسریبرونریزی کلیدهای اتاقها
@@ -2601,8 +2601,8 @@
گشودن تنظیماتتمامی گپهانمایش تمامی نشستها (ن۲، دحت)
- برای امنیت بیشتر، نشستهایتان را تأیید و از هر نشستی که تشخیصش نمیدهید یا دیگر استفاده نمیکنید خارج شوید.
- دیگر نشستها
+ برای امنیت بیشتر، نشستهایتان را تأیید و از هر نشستی که تشخیصش نمیدهید یا دیگر استفاده نمیکنید خارج شوید.
+ دیگر نشستهانشستهاگشودن سیاههٔ فضاهاایجاد اتاق یا گفتوگویی جدید
@@ -2622,11 +2622,8 @@
تأیید نشده · آخرین فعّالیت %1$sتأیید شده · آخرین فعّالیت %1$sدیدن همه (%1$d)
- نشست کنونیدیدن جزییاتتأیید نشست
- نشست کنونیتان را برای پیامرسانی امن بهبود یافته تأیید کنید.
- نشست کنونیتان برای پیامرسانی امن آماده است.نشست تأیید نشدهنشست تأیید شدهگونهٔ افزاره ناشناخته
@@ -2636,4 +2633,71 @@
متأسفانه این اتاق پیدا نشد.
\nلطفاً بعداً دوباره تلاش کنید.%sدعوتها
-
+ زدن بالا سمت چپ برای دیدن گزینهٔ بازخورد.
+ دسترسی به فضاهایتان (پایین سمت چپ) سریعتر و سادهتر از همیشه.
+ برای سادهسازی ${app_name} زبانهها اختیاری شدهاند. مدیریت با استفاده از فهرست بالا سمت چپ.
+ این جایی است که پیامهای ناخواندهتان در صورت وجود ظاهر خواهند شد.
+ کارهٔ گپ امن یکپارچه برای گروهها، دوستان و سازمانها. برای آغاز، گپی ساخته یا به اتاقی بپیوندید.
+ فضاها راهی جدید برای گروهبندی اتاقها و افراد است. با استفاده از دکمهٔ پایین سمت چپ فضایی ساخته یا اتاقی را بیفزایید.
+ %s
+\nکمی خالی به نظر میرسد.
+
+ در نظر گرفتن خروج از نشستهای قدیمی (۱ روز یا بیشتر) که دیگر استفاده نمیکنید.
+ در نظر گرفتن خروج از نشستهای قدیمی (%1$d روز یا بیشتر) که دیگر استفاده نمیکنید.
+
+ تأیید یا خروج از نشستهای تأییدنشده.
+ بهبود امنیت حسابتان با پیروی از این توصیهها.
+
+ غیرفعّال برای بیش از %1$d روز (%2$s)
+ غیرفعّال برای بیش از %1$d روز (%2$s)
+
+ این جایی است که درخواستها و دعوتهای جدیدتان خواهند بود.
+ فضاها راهی جدید برای گروهبندی اتاقها و افراد است. برای آغاز، فضایی بسازید.
+ بیازماییدش
+ دادن بازخورد
+ دسترسی به فضاها
+ به نمایی جدید خوش آمدید!
+ چیزی برای گزارش نیست.
+ %s
+\nبه ${app_name} خوش آمدی.
+ نشستهای غیرفعّال
+ نشستهای تأیید نشده
+ توصیههای امنیتی
+ چیز جدیدی نیست.
+ هنوز فضایی وجود ندارد.
+ جمع کردن فرزندان %s
+ گسترش فرزندان %s
+ تغییر فضا
+ نشانی آیپی
+ واپسین فعّالیت
+ نام نشست
+ اطّلاعات برنامه، افزاره و فعّالیت.
+ جزییات نشست
+ پاکسازی پالایه
+ هیچ نشست غیرفعّالی پیدا نشد.
+ هیچ نشست تأیید نشدهای پیدا نشد.
+ هیچ نشست تأیید نشدهای پیدا نشد.
+ غیرفعّال
+ تأیید نشده
+ برای بهترین امنیت، از هرنشستی که تشخیصش نمیدهید یا دیگر استفاده نمیکنید، خارج شوید.
+ تأیید شده
+ پالایه
+ غیرفعّال
+ نا آماده برای پیامرسانی امن
+ تأیید نشده
+ آمادهٔ پیامرسانی امن
+ تأیید شده
+ تمامی نشستها
+ پالایه
+ آخرین فعّالیت %1$s
+ افزاره
+ نشست
+ نشست کنونی
+ برای بهترین امنیت و اطمینان این نشست را تأیید کرده یا خارج شوید.
+ تأیید نشست کنونیتان برای پیامرسانی امن.
+ این نشست برای پیامرسانی امن آماده است.
+ نشست کنونیتان برای پیامرسانی امن آماده است.
+ ایجاد پیام خصوصی فقط در نخستین پیام
+ المنتی ساده شده با زبانههای انتخابی
+ به کار انداختن چینش جدید
+
\ No newline at end of file
diff --git a/library/ui-strings/src/main/res/values-fi/strings.xml b/library/ui-strings/src/main/res/values-fi/strings.xml
index fde2502ae0..a576e7f0dc 100644
--- a/library/ui-strings/src/main/res/values-fi/strings.xml
+++ b/library/ui-strings/src/main/res/values-fi/strings.xml
@@ -366,7 +366,7 @@
Kumoa pääosoitteeksi asettaminenSalauksenpurkuvirheJulkinen nimi
- Istunnon tunnus
+ Istunnon tunnusIstunnon avainVie salatun huoneen avaimetVie huoneen avaimet
diff --git a/library/ui-strings/src/main/res/values-fr-rCA/strings.xml b/library/ui-strings/src/main/res/values-fr-rCA/strings.xml
index 29a618f415..94db2935a7 100644
--- a/library/ui-strings/src/main/res/values-fr-rCA/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr-rCA/strings.xml
@@ -778,7 +778,7 @@
Exporter les clés des salonsExporter les clés E2E des salonsClé de la session
- Identifiant de session
+ Identifiant de sessionNom publicErreur de déchiffrementThème
diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml
index 55b5f88134..5a19ccf2da 100644
--- a/library/ui-strings/src/main/res/values-fr/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings.xml
@@ -346,7 +346,7 @@
Désactiver comme adresse principaleErreur de déchiffrementNom public
- Identifiant de session
+ Identifiant de sessionClé de la sessionExporter les clés E2E des salonsExporter les clés des salons
@@ -2601,8 +2601,8 @@
Ouvrir les paramètresToutes les conversationsAfficher toutes les sessions (V2, en cours)
- Pour une meilleure sécurité, vérifiez vos sessions et déconnectez toutes les sessions que vous ne connaissez pas ou que vous n’utilisez plus.
- Autres sessions
+ Pour une meilleure sécurité, vérifiez vos sessions et déconnectez toutes les sessions que vous ne connaissez pas ou que vous n’utilisez plus.
+ Autres sessionsSessionsOuvrir la liste des espacesCréer une nouvelle conversation ou salon
@@ -2622,11 +2622,8 @@
Non vérifiée · Dernière activité %1$sVérifié · Dernière activité %1$sTout voir (%1$d)
- Cette sessionVoir les détailsVérifier la session
- Vérifiez votre session pour une sécurité renforcée de votre messagerie.
- Votre session est prête pour l’envoi de messages sécurisés.Session non vérifiéeSession vérifiéeType de périphérique inconnu
@@ -2636,4 +2633,39 @@
Désolé, impossible de trouver ce salon.
\nVeuillez réessayer plus tard.%sInvitations
+ Essayez
+ Appuyez en haut à droite pour les options des avis.
+ Donner mon avis
+ Accédez à vos espaces (en bas à droite) plus rapidement et facilement qu’avant.
+ Accéder aux espaces
+ Pour simplifier Element, les onglets sont désormais facultatifs. Gérez les depuis le menu en haut à droite.
+ Bienvenu dans une nouvelle vue !
+ C\'est ici que vos messages non-lus s’afficheront lorsque vous en aurez.
+ Rien à signaler.
+ La messagerie sécurisée tout-en-un pour les équipes, les amis, et les organisations. Créez une discussion ou rejoignez un salon pour démarrer.
+ Bienvenue dans ${app_name},
+\n%s.
+ Les espaces sont un nouveau moyen de grouper les salons et les gens. Ajoutez un salon, ou créez en un nouveau à l’aide du bouton en bas à droite.
+ %s
+\na l’air un peu vide.
+
+ Pensez à vous déconnecter des anciennes sessions (%1$d jour ou plus) que vous n’utilisez plus.
+ Pensez à vous déconnecter des anciennes sessions (%1$d jours ou plus) que vous n’utilisez plus.
+
+ Sessions inactives
+ Vérifier ou déconnecter les sessions non vérifiées.
+ Sessions non vérifiées
+ Améliorez la sécurité de votre compte à l’aide de ces recommandations.
+ Recommandations de sécurité
+
+ Inactif depuis %1$d+ jour (%2$s)
+ Inactif depuis %1$d+ jours (%2$s)
+
+ C’est l’endroit où se trouveront vos nouvelles requêtes et invitations.
+ Rien de neuf.
+ Les espaces sont un nouveau moyen de regrouper les salons et les gens. Créez un espace pour commencer.
+ Pas d’espace pour l’instant.
+ Réduire %s enfants
+ Développer %s enfants
+ Changer d’espace
diff --git a/library/ui-strings/src/main/res/values-gl/strings.xml b/library/ui-strings/src/main/res/values-gl/strings.xml
index e6d26a63e5..c1e4e40a81 100644
--- a/library/ui-strings/src/main/res/values-gl/strings.xml
+++ b/library/ui-strings/src/main/res/values-gl/strings.xml
@@ -380,7 +380,7 @@
TemaFallo ao descifrarNome do dispositivo
- ID de sesión
+ ID de sesiónChave do dispositivoExportar chaves E2E da salaExportar chaves da sala
diff --git a/library/ui-strings/src/main/res/values-hr/strings.xml b/library/ui-strings/src/main/res/values-hr/strings.xml
index dc5930b933..6d52e5cd96 100644
--- a/library/ui-strings/src/main/res/values-hr/strings.xml
+++ b/library/ui-strings/src/main/res/values-hr/strings.xml
@@ -572,7 +572,7 @@
TemaGreška u dešifriranjuJavni naziv
- Identitet
+ IdentitetKljuč sesijeIzvezi sobne ključeve za E2EIzvezi sobne ključeve
diff --git a/library/ui-strings/src/main/res/values-hu/strings.xml b/library/ui-strings/src/main/res/values-hu/strings.xml
index af8bf26b2e..3068556fe4 100644
--- a/library/ui-strings/src/main/res/values-hu/strings.xml
+++ b/library/ui-strings/src/main/res/values-hu/strings.xml
@@ -351,7 +351,7 @@
Kiszedés fő címek közülVisszafejtés hibaNyilvános név
- Munkamenet-azonosító
+ Munkamenet-azonosítóMunkamenet kulcsE2E szoba kulcsok exportálásaSzoba kulcsok exportálása
@@ -2603,11 +2603,8 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
Nem ellenőrzött - Utolsó aktivitás %1$sEllenőrzött - Utolsó tevékenység %1$sÖsszes megtekintése (%1$d)
- Jelenlegi munkamenetRészletek megtekintéseMunkamenet hitelesítése
- Az aktuális munkamenet készen áll a biztonságos üzenetküldésre.
- Az aktuális munkamenet készen áll a biztonságos üzenetküldésre.Ellenőrizetlen munkamenetEllenőrzött munkamenetIsmeretlen eszköztípus
@@ -2615,12 +2612,12 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
WebMobilMinden munkamenet megjelenítése (V2, WIP)
- A legjobb biztonság érdekében ellenőrizd a munkameneteket, és jelentkezz ki minden olyan munkamenetből, melyet már nem ismersz fel vagy nem használsz.
- Más munkamenetek
+ A legjobb biztonság érdekében ellenőrizd a munkameneteket, és jelentkezz ki minden olyan munkamenetből, melyet már nem ismersz fel vagy nem használsz.
+ Más munkamenetekMunkamenetekNyitott területek listájaÚj beszélgetés vagy szoba létrehozása
- Résztvevők
+ EmberekKedvencekOlvasatlanMind
@@ -2636,4 +2633,39 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
Szobák felfedezéseSzoba létrehozásaChat indítása
+ Próbáld ki
+ Visszajelzés adása
+ A terekhez való hozzáférés (jobbra lent) gyorsabb és egyszerűbb mint valaha.
+ Hozzáférés a terekhez
+ ${app_name} egyszerűsítéséhez a lapok mostantól választhatók. Beállítani a jobb felső menüből lehet.
+ Üdv az új kinézetben!
+ Ez az a hely ahol az olvasatlan üzeneteid megjelennek, ha lesznek.
+ Nincs semmi említésre méltó.
+ A minden-egyben biztonságos csevegő alkalmazás csapatoknak, barátoknak és szervezeteknek. Kezd egy csevegést vagy lépj be egy meglévő szobába kezdésnek.
+ Üdv itt: ${app_name}!
+\n%s.
+ Szobák és emberek csoportokba rendezésének új mondja a terek használata. Létező szoba hozzáadása vagy új készítése a jobb alsó gombbal.
+ %s
+\nkicsit üresnek tűnik.
+ Nem aktív munkamenetek
+ Ellenőrizd vagy jelentkezz ki az ellenőrizetlen munkamenetekből.
+ Meg nem erősített munkamenetek
+ Javítsa a fiókja biztonságát azzal, hogy követi a következő javaslatokat.
+ Biztonsági javaslatok
+ Semmi új.
+ Terekkel lehet szobákat és személyeket csoportokba rendezni. Készíts egyet indulásnak.
+ Nincsenek terek egyelőre.
+ %s összezárása
+ %s kinyitása
+ Tér cseréje
+ A visszajelzési lehetőségekhez koppint jobb felső sarokba.
+
+ Fontold meg, hogy a régi már nem használt (%1$d napja vagy régebben) munkamenetből kijelentkezel.
+ Fontold meg, hogy a régi már nem használt (%1$d napja vagy régebben) munkamenetből kijelentkezel.
+
+
+ %1$d+ napja inaktív (%2$s)
+ %1$d+ napja inaktív (%2$s)
+
+ Itt láthatók a meghívók és elvégzendő műveletek.
diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index d1e68b4529..3b30950bd1 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -301,7 +301,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
TemaKesalahan dekripsiNama perangkat
- ID Sesi
+ ID SesiKunci perangkatEkspor kunci ruangan terenkripsiEkspor ruangan kunci
@@ -593,7 +593,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
Abaikan penggunaTurunkanAnda tidak akan dapat membatalkan perubahan ini karena Anda menurunkan diri sendiri, jika Anda adalah pengguna istimewa terakhir di ruangan itu akan tidak mungkin untuk mendapatkan kembali hak istimewa.
- Turunkan dirimu\?
+ Turunkan diri Anda\?Batalkan undanganRuangan ini tidak umum. Anda tidak akan dapat bergabung kembali tanpa undangan.Izinkan untuk mengakses kontak.
@@ -2553,8 +2553,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
Email belum diverifikasi, periksa kotak masuk AndaSemua ObrolanTampilkan Semua Sesi (V2, Dalam Pengembangan)
- Untuk keamanan terbaik, verifikasi sesi Anda dan keluarkan sesi apa pun yang Anda tidak kenal atau Anda tidak gunakan lagi.
- Sesi lainnya
+ Untuk keamanan terbaik, verifikasi sesi Anda dan keluarkan sesi apa pun yang Anda tidak kenal atau Anda tidak gunakan lagi.
+ Sesi lainnyaSesiBuka daftar spaceBuat percakapan atau ruangan baru
@@ -2576,11 +2576,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
Belum diverifikasi · Aktivitas terakhir %1$sTerverifikasi · Aktivitas terakhir %1$sTampilkan Semua (%1$d)
- Sesi Saat IniTampilkan DetailVerifikasi Sesi
- Verifikasi sesi Anda saat ini untuk perpesanan yang aman.
- Sesi Anda saat ini siap untuk perpesanan yang aman.Sesi belum diverifikasiSesi terverifikasiTipe perangkat tidak diketahui
@@ -2588,4 +2585,37 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
WebPonselUndangan
+ Coba
+ Ketuk kanan atas untuk melihat opsi untuk memberikan masukan.
+ Beri Masukan
+ Aplikasi obrolan aman untuk tim, teman, dan organisasi. Buat sebuah obrolan, atau bergabung ke ruangan yang sudah ada, untuk memulai.
+ Akses Space Anda (di kanan bawah) dengan lebih cepat dan lebih mudah dari sebelumnya.
+ Akses Space
+ Untuk membuat ${app_name} Anda lebih sederhana, fitur tab sekarang opsional. Kelola menggunakan menu kanan atas.
+ Selamat datang di tampilan yang baru!
+ Ini di mana pesan Anda yang belum dibaca akan ditampilkan, ketika Anda menerimanya.
+ Tidak ada untuk dilaporkan.
+ Selamat datang di ${app_name},
+\n%s.
+ Space adalah cara baru untuk mengelompokkan ruangan dan orang. Tambahkan ruangan yang sudah ada, atau buat yang baru, dengan tombol di kanan bawah.
+ %s
+\nkelihatannya masih kosong.
+
+ Pertimbangkan untuk mengeluarkan sesi lawas (%1$d hari atau lebih) yang Anda tidak gunakan lagi.
+
+ Sesi yang tidak aktif
+ Verifikasi atau keluarkan sesi yang belum diverifikasi.
+ Sesi yang belum diverifikasi
+ Perbaiki keamanan akun Anda dengan mengikuti saran berikut.
+ Saran keamanan
+
+ Tidak aktif selama %1$d+ hari (%2$s)
+
+ Ini di mana permintaan dan undangan baru Anda akan berada.
+ Belum ada yang baru.
+ Space adalah cara baru untuk mengelompokkan ruangan dan orang. Buat sebuah space untuk memulai.
+ Belum ada space.
+ Tutup %s anak
+ Buka %s anak
+ Buat Space
diff --git a/library/ui-strings/src/main/res/values-is/strings.xml b/library/ui-strings/src/main/res/values-is/strings.xml
index 7818761145..d25d66bfba 100644
--- a/library/ui-strings/src/main/res/values-is/strings.xml
+++ b/library/ui-strings/src/main/res/values-is/strings.xml
@@ -193,7 +193,7 @@
ÞemaAfkóðunarvillaHeiti tækis
- Auðkenni setu
+ Auðkenni setuDulritunarlykill setuFlytja útSettu inn lykilsetningu
diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml
index ecb29d1586..b7b0fe91af 100644
--- a/library/ui-strings/src/main/res/values-it/strings.xml
+++ b/library/ui-strings/src/main/res/values-it/strings.xml
@@ -430,7 +430,7 @@
TemaErrore di decriptazioneNome pubblico
- ID sessione
+ ID sessioneChiave sessioneEsporta le chiavi di crittografia E2E delle stanzeEsporta le chiavi delle stanze
@@ -2592,8 +2592,8 @@
Apri le impostazioniTutte le chatMostra tutte le sessioni (V2, WIP)
- Per una maggiore sicurezza, verifica le tue sessioni e disconnetti quelle che non riconosci o che non usi più.
- Altre sessioni
+ Per una maggiore sicurezza, verifica le tue sessioni e disconnetti quelle che non riconosci o che non usi più.
+ Altre sessioniSessioniApri elenco spaziCrea una nuova conversazione o stanza
@@ -2613,11 +2613,8 @@
Non verificata · Ultima attività %1$sVerificata · Ultima attività %1$sVedi tutte (%1$d)
- Sessione attualeVedi dettagliVerifica la sessione
- Verifica la tua sessione attuale per messaggi più sicuri.
- La tua sessione attuale è pronta per i messaggi sicuri.Sessione non verificataSessione verificataTipo di dispositivo sconosciuto
@@ -2627,4 +2624,39 @@
Spiacenti, questa stanza non è stata trovata.
\nRiprova più tardi.%sInviti
+ Provalo
+ Tocca in alto a destra per vedere l\'opzione feedback.
+ Invia un feedback
+ Accedi ai tuoi spazi (in basso a destra) più velocemente e più facilmente che mai.
+ Accedi agli spazi
+ Per semplificare ${app_name}, le schede ora sono opzionali. Gestiscile usando il menu in alto a destra.
+ Benvenuti ad una nuova panoramica!
+ Qui è dove verranno mostrati i messaggi non letti, quando ne avrai qualcuno.
+ Niente da segnalare.
+ L\'app di chat tutto-in-uno per team, amici e organizzazioni. Inizia una conversazione o entra in una stanza esistente per cominciare.
+ Benvenuto/a in ${app_name},
+\n%s.
+ Gli spazi sono un modo nuovo di raggruppare stanze e persone. Aggiungi una stanza esistente, o creane una nuova usando il pulsante in basso a destra.
+ %s
+\nsembra un po\' vuoto.
+
+ Considera di disconnettere le sessioni vecchie (%1$d giorno o più) che non usi più.
+ Considera di disconnettere le sessioni vecchie (%1$d giorni o più) che non usi più.
+
+ Sessioni inattive
+ Verifica o disconnetti le sessioni non verificate.
+ Sessioni non verificate
+ Migliora la sicurezza del tuo account seguendo questi consigli.
+ Consigli di sicurezza
+
+ Inattivo da %1$d+ giorno (%2$s)
+ Inattivo da %1$d+ giorni (%2$s)
+
+ Qui è dove troverai le nuove richieste e gli inviti.
+ Niente di nuovo.
+ Gli spazi sono un modo nuovo di raggruppare stanze e persone. Crea uno spazio per iniziare.
+ Ancora nessuno spazio.
+ Riduci contenuto di %s
+ Espandi contenuto di %s
+ Cambia spazio
diff --git a/library/ui-strings/src/main/res/values-iw/strings.xml b/library/ui-strings/src/main/res/values-iw/strings.xml
index 6d9533852b..ff19310c8e 100644
--- a/library/ui-strings/src/main/res/values-iw/strings.xml
+++ b/library/ui-strings/src/main/res/values-iw/strings.xml
@@ -542,7 +542,7 @@
יצא מפתחות חדרייצא מפתחות חדר E2Eמזהה מפתח
- מזהה מושב
+ מזהה מושבשם ציבורישגיאת פענוחערכת נושא
diff --git a/library/ui-strings/src/main/res/values-ja/strings.xml b/library/ui-strings/src/main/res/values-ja/strings.xml
index b781e4d7f0..3e817e398c 100644
--- a/library/ui-strings/src/main/res/values-ja/strings.xml
+++ b/library/ui-strings/src/main/res/values-ja/strings.xml
@@ -197,7 +197,7 @@
これらは予期しない不具合が生じるかもしれない実験的機能です。慎重に使用してください。メインアドレスとして設定メインアドレスとしての設定を解除
- セッションID
+ セッションID文字の大きさとても小さい小さい
diff --git a/library/ui-strings/src/main/res/values-kab/strings.xml b/library/ui-strings/src/main/res/values-kab/strings.xml
index a79b72efde..353fb99f53 100644
--- a/library/ui-strings/src/main/res/values-kab/strings.xml
+++ b/library/ui-strings/src/main/res/values-kab/strings.xml
@@ -291,7 +291,7 @@
TalqaytTinariminAsentel
- Asulay n tqimit
+ Asulay n tqimitTasarut n tɣimitSifeḍ tisura n texxamt E2ESifeḍ tisura n texxamt
diff --git a/library/ui-strings/src/main/res/values-ko/strings.xml b/library/ui-strings/src/main/res/values-ko/strings.xml
index ba0cbe5abd..37e8849fa8 100644
--- a/library/ui-strings/src/main/res/values-ko/strings.xml
+++ b/library/ui-strings/src/main/res/values-ko/strings.xml
@@ -431,7 +431,7 @@
테마암호 복호화 오류공개 이름
- ID
+ ID기기 키종단간 암호화 방 키 내보내기방 키 내보내기
diff --git a/library/ui-strings/src/main/res/values-lo/strings.xml b/library/ui-strings/src/main/res/values-lo/strings.xml
index 1a9a2820b8..a92adb0225 100644
--- a/library/ui-strings/src/main/res/values-lo/strings.xml
+++ b/library/ui-strings/src/main/res/values-lo/strings.xml
@@ -909,7 +909,7 @@
ສົ່ງອອກກະແຈຫ້ອງສົ່ງອອກກະແຈຫ້ອງ E2Eລະຫັດລະບົບ
- ID ລະບົບ
+ ID ລະບົບຊື່ສາທາລະນະການຖອດລະຫັດຜິດພາດຫົວຂໍ້
diff --git a/library/ui-strings/src/main/res/values-lt/strings.xml b/library/ui-strings/src/main/res/values-lt/strings.xml
index c33f8257c6..adfc70c36e 100644
--- a/library/ui-strings/src/main/res/values-lt/strings.xml
+++ b/library/ui-strings/src/main/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-
+Naudotojo %s pakvietimasJūs prisijungėte prie kambario%1$s prisijungė prie kambario
@@ -668,12 +668,12 @@
Pridėjote %1$s valdiklį%1$s pridėjo %2$s valdiklį%1$s išsiuntė kvietimą %2$s prisijungti prie kambario
- %1$s atšaukė %2$s kvietimą prisijungti prie kambario
+ %1$s atšaukė %2$s pakvietimą prisijungti prie kambarioPriėmėte kvietimą į %1$s%1$s priėmė kvietimą į %2$sAtšaukėte kvietimą %1$s%1$s atšaukė %2$s kvietimą
- Atšaukėte %1$s kvietimą prisijungti prie kambario
+ Atšaukėte %1$s pakvietimą prisijungti prie kambarioPakeitimų nėra.• Serveriai atitinkantys %s buvo pašalinti iš leidžiamų sąrašo.• Serveriai atitinkantys %s dabar yra leidžiami.
@@ -681,4 +681,729 @@
• Serveriai atitinkantys %s dabar yra uždrausti.• Serveriai atitinkantys %s yra leidžiami.• Serveriai atitinkantys %s yra uždrausti.
-
\ No newline at end of file
+ Siųsti m.room.server_acl įvykius
+ Užblokuoti naudotoją
+ Naudotojas bus pašalintas iš šio kambario.
+\n
+\nKad jis negalėtų prisijungti dar kartą, turėtumėte jį užblokuoti.
+ Pašalinimo priežastis
+ Pašalinti naudotoją
+ Ar tikrai norite atšaukti kvietimą šiam naudotojui\?
+ Atšaukti kvietimą
+ Nebeignoruoti
+ Nebeignoruojant šio naudotojo, vėl bus rodomos visos jo žinutės.
+ Nebeignoruoti naudotojo
+ Ignoruodami šį naudotoją nebematysite jo žinučių bendruose kambariuose.
+\n
+\nŠį veiksmą bet kada galite atšaukti bendruosiuose nustatymuose.
+ Ignoruoti naudotoją
+ Pažeminti
+ Negalėsite atšaukti šio pakeitimo, nes pažeminsite save, o jei esate paskutinis privilegijuotas naudotojas kambaryje, bus neįmanoma susigrąžinti privilegijų.
+ Nuleisti save į žemesnes pareigas\?
+ Šio pakeitimo atšaukti negalėsite, nes padidinsite naudotojo galią, kad jis turėtų tokį patį galios lygį, kaip ir jūs pats.
+\nAr esate tikri\?
+ Paminėti
+ Pašalinti iš pokalbio
+ Atšaukti kvietimą
+ Tiesioginiai pokalbiai
+ Šis kambarys nėra viešas. Negalėsite vėl prisijungti be kvietimo.
+ Ar tikrai norite palikti kambarį\?
+ Palikti kambarį
+
+ %d narys
+ %d nariai
+ %d narių
+
+ Pereiti prie neskaitytų
+ Nariai
+ Suteikite leidimą prieiti prie savo kontaktų.
+ Jei norite nuskaityti QR kodą, turite leisti kameros prieigą.
+ ${app_name} reikia leidimo prieiti prie jūsų kameros ir mikrofono, kad galėtumėte atlikti vaizdo skambučius.
+\n
+\nKad galėtumėte skambinti, kituose iškylančiuose languose leiskite prieigą.
+ Čia bus pateikiamos naujos užklausos ir kvietimai.
+ Nieko naujo.
+ Pradėti pokalbį
+ • Serveriai atitinkantys IP dabar yra užblokuoti.
+ • Serveriai atitinkantys IP dabar yra leidžiami.
+
+ %d serverių ACL pakeitimas
+ %d serverių ACL pakeitimai
+ %d serverių ACL pakeitimų
+
+ Pakeitėte serverių ACL šiam kambariui.
+ %s pakeitė serverių ACL šiam kambariui.
+ • Serveriai atitinkantys IP yra užblokuoti.
+ • Serveriai atitinkantys IP yra leidžiami.
+ Nustatėte serverių ACL šiam kambariui.
+ %s nustatė serverių ACL šiam kambariui.
+ Išbandyti
+ Bakstelėkite viršuje dešinėje, kad pamatytumėte atsiliepimų parinktį.
+ Pateikite atsiliepimus
+ Pasiekti erdves
+ Pasiekite erdves (apačioje dešinėje) greičiau ir paprasčiau nei bet kada anksčiau.
+ Siekiant supaprastinti jūsų ${app_name}, skirtukai dabar yra neprivalomi. Tvarkykite juos naudodami viršutinį dešinės pusės meniu.
+ Sveiki atvykę į naują vaizdą!
+ Čia bus rodomos jūsų neperskaitytos žinutės, kai jų turėsite.
+ Nėra apie ką pranešti.
+ \"viskas viename\" saugi pokalbių programėlė komandoms, draugams ir organizacijoms. Sukurkite pokalbį arba prisijunkite prie esamo kambario ir pradėkite.
+ Sveiki atvykę į ${app_name},
+\n%s.
+ Erdvės - tai naujas kambarių ir žmonių grupavimo būdas. Pridėkite esamą kambarį arba sukurkite naują naudodami apatinį dešinįjį mygtuką.
+ %s
+\natrodo šiek tiek tuščia.
+
+ Apsvarstykite galimybę atsijungti iš senų sesijų (%1$d diena ar daugiau), kurių nebenaudojate.
+ Apsvarstykite galimybę atsijungti iš senų sesijų (%1$d dienos ar daugiau), kurių nebenaudojate.
+ Apsvarstykite galimybę atsijungti iš senų sesijų (%1$d dienų ar daugiau), kurių nebenaudojate.
+
+ Neaktyvios sesijos
+ Patvirtinkite nepatvirtintas sesijas arba atjunkite jas.
+ Nepatvirtintos sesijos
+ Pagerinkite savo paskyros saugumą laikydamiesi šių rekomendacijų.
+ Saugumo rekomendacijos
+
+ Neaktyvus %1$d+ dieną (%2$s)
+ Neaktyvus %1$d+ dienas (%2$s)
+ Neaktyvus %1$d+ dienų (%2$s)
+
+ Nepatvirtinta · Paskutinė veikla %1$s
+ Patvirtinta · Paskutinė veikla %1$s
+ Peržiūrėti visas (%1$d)
+ Peržiūrėti detales
+ Patvirtinti sesiją
+ Nepatvirtinta sesija
+ Patvirtinta sesija
+ Nežinomas įrenginio tipas
+ Stalinis kompiuteris
+ Naršyklė
+ Mobilus
+
+ %d žinutė pašalinta
+ %d žinutės pašalintos
+ %d žinučių pašalinta
+
+ Įjungti vietos bendrinimą
+ Atkreipkite dėmesį, kad tai yra laboratorinė funkcija, kuri įgyvendinama laikinai. Tai reiškia, kad negalėsite ištrinti savo buvimo vietos istorijos, o pažengę naudotojai galės matyti jūsų buvimo vietos istoriją net tada, kai nustosite bendrinti savo tiesioginę buvimo vietą su šiuo kambariu.
+ Tiesioginės buvimo vietos bendrinimas
+ Dabartiniai vartai: %s
+ Vartai
+ Nepavyksta rasti galinio taško.
+ Dabartinis galinis taškas: %s
+ Galinis taškas
+ Šiuo metu naudojamas %s.
+ Metodas
+
+ Rastas %d metodas.
+ Rasti %d metodai.
+ Rasti %d metodų.
+
+ Nerastas joks kitas metodas, išskyrus foninį sinchronizavimą.
+ Nerastas joks kitas būdas, išskyrus Google Play paslaugas.
+ Galimi metodai
+ Pranešimo metodas
+ Foninis sinchronizavimas
+ Google Paslaugos
+ Pasirinkite, kaip gauti pranešimus
+ Vyksta ekrano bendrinimas
+ ${app_name} Ekrano bendrinimas
+ Kambario pranešimas
+ Naudotojai
+ Pranešti visam kambariui
+
+ %1$d daugiau
+ %1$d daugiau
+ %1$d daugiau
+
+ Rodyti mažiau
+ Bendrinti vietą
+ Kurti apklausą
+ Atidaryti kontaktus
+ Siųsti lipduką
+ Įkelti failą
+ Reikalingas atnaujinimas
+ Atnaujinti
+ Būkite kantrūs, tai gali užtrukti.
+ Prisijungti prie pakaitinio kambario
+ Nepavadintas kambarys
+ Jūs esate vienintelis šios erdvės administratorius. Jei ją paliksite, tai reikš, kad niekas jos nebekontroliuos.
+ Negalėsite prisijungti vėl, nebent būsite pakviestas iš naujo.
+ Jūs esate vienintelis čia esantis asmuo. Jei paliksite, ateityje niekas, įskaitant jus, negalės prisijungti.
+ Ar tikrai norite palikti %s\?
+ Nepalikti nė vieno
+ Palikti visus
+ Dalykai šioje erdvėje
+ Vistiek prisijungti
+ Kol kas praleisti
+ Baigti nustatyti atradimą.
+ Šiuo metu šis pseudonimas neprieinamas.
+\nPabandykite vėliau arba paprašykite kambario administratoriaus patikrinti, ar turite prieigą.
+ Atradimas (%s)
+ Užbaigti sąranka
+ Kvieskite el. paštu, ieškokite kontaktų ir daugiau…
+ Jie nebus %s dalis
+ Tik į šį kambarį
+ Šiuo metu nenaudojate tapatybės serverio. Norėdami pakviesti komandos draugus ir būti jų atrandami, sukonfigūruokite jį toliau.
+ Prisijungti prie erdvės
+ Sukurti erdvę
+ Prisijunkite prie mano erdvės %1$s %2$s
+ Jie galės tyrinėti %s
+ Pakvietimas į %s
+ Kviesti žmones į savo erdvę
+ Aprašymas
+ Kuriama erdvė…
+ Atsitiktinis
+ Bendra
+ Sukurkime kiekvienai iš jų po kambarį. Vėliau galite pridėti ir daugiau, įskaitant jau esamus.
+ Su kokiais dalykais dirbate\?
+ Užtikrinkite, kad prieigą prie %s kompanijos turėtų tinkami žmonės. Vėliau galite pakviesti daugiau.
+ Kas yra jūsų komandos draugai\?
+ Mes sukursime joms kambarius. Vėliau galėsite pridėti ir daugiau.
+ Kokias diskusijas norite turėti %s\?
+ Suteikite jai pavadinimą, kad galėtumėte tęsti.
+ Pridėkite šiek tiek detalių, kad žmonės galėtų ją atpažinti. Jas galite keisti bet kuriuo metu.
+ Įtraukite keletą detalių, kurios padės išsiskirti. Jas galite keisti bet kuriuo metu.
+ Sukurti erdvę
+ Privati erdvė jums & jūsų komandos draugams
+ Aš ir komandos draugai
+ Privati erdvė kambariams organizuoti
+ Tik aš
+ Užtikrinkite, kad prieigą prie %s turėtų tinkami asmenys.
+ Su kuo dirbate\?
+ Norėdami prisijungti prie esamos erdvės, turite gauti kvietimą.
+ Galite tai pakeisti vėliau
+ Kokio tipo erdvę norite sukurti\?
+ Jūsų privati erdvė
+ Jūsų vieša erdvė
+ Pridėti erdvę
+ Privati erdvė
+ Vieša erdvė
+ Prisijungti prie erdvės su nurodytu id
+ Pridėti prie nurodytos erdvės
+ Sukurti erdvę
+ Neteisingas naudotojo vardas ir (arba) slaptažodis. Įvestas slaptažodis prasideda arba baigiasi tarpais, patikrinkite jį.
+ Kuriama erdvė…
+ Erdvės adresas
+ Negalima atidaryti šios nuorodos: bendruomenės buvo pakeistos erdvėmis
+ Atidaryti erdvių sąrašą
+ Naudojate erdvių beta versiją. Jūsų atsiliepimai padės parengti kitas versijas. Jūsų platforma ir naudotojo vardas bus pažymėti, kad galėtume kuo geriau pasinaudoti jūsų atsiliepimais.
+ Atsiliepimai apie erdves
+ Sukurti naują erdvę
+ Kitos erdvės ar kambariai, apie kuriuos galbūt nežinote
+ Erdvė, apie kurią žinote, kurioje yra šis kambarys
+ Bakstelėkite, kad redaguoti erdves
+ Pasirinkti erdves
+ Nuspręskite, kurios erdvės gali prieit prie šio kambario. Jei pasirinkta erdvė, jos nariai galės rasti kambario pavadinimą ir prie jo prisijungti.
+ Erdvės, kurios gali pasiekti
+ Leisti erdvės nariams rasti ir pasiekti.
+ Erdvės %s nariai gali rasti, peržiūrėti ir prisijungti.
+ Kiekvienas, esantis erdvėje, kurioje yra šis kambarys, gali jį rasti ir prie jo prisijungti. Tik šio kambario administratoriai gali jį įtraukti į erdvę.
+ Tik erdvės nariams
+ Bet kas gali rasti šią erdvę ir prisijungti
+ Peržiūrėti ir valdyti šios erdvės adresus.
+ Erdvės adresai
+ Erdvės prieiga
+
+ %1$s ir %2$d kitas
+ %1$s ir %2$d kiti
+ %1$s ir %2$d kitų
+
+ Atnaujinti erdvę
+ Keisti erdvės pavadinimą
+ Įjungti erdvės šifravimą
+ Keisti erdvės pagrindinį adresą
+ Keisti erdvės avatarą
+ Neturite leidimo atnaujinti roles, reikalingas įvairioms šios erdvės dalims keisti
+ Pasirinkite roles, reikalingas įvairioms šios erdvės dalims keisti
+ Peržiūrėkite ir atnaujinkite roles, reikalingas įvairioms erdvės dalims keisti.
+ Erdvės leidimai
+ Atblokavus naudotoją, jis vėl galės prisijungti prie erdvės.
+ Užblokavus naudotoją, jis bus pašalintas iš šios erdvės ir negalės prisijungti dar kartą.
+ Naudotojas bus pašalintas iš šios erdvės.
+\n
+\nKad jis negalėtų prisijungti dar kartą, turėtumėte jį užblokuoti.
+ Erdvės
+ Erdvės - tai naujas kambarių ir žmonių grupavimo būdas. Sukurkite erdvę ir pradėkite.
+ Erdvių dar nėra.
+ Erdvės
+ Keisti erdvę
+ Atsarginė kopija turi galiojantį parašą iš nepatvirtintos sesijos %s
+ Atsarginė kopija turi galiojantį parašą iš patvirtintos sesijos %s.
+ Atsarginė kopija turi galiojantį šios sesijos parašą.
+ Atsarginė kopija turi galiojantį šio naudotojo parašą.
+ Atsarginė kopija turi nežinomos sesijos parašą su ID %s.
+ Jūsų raktų atsarginės kopijos iš šios sesijos nedaromos.
+ Šioje sesijoje raktų atsarginė kopija nėra aktyvi.
+ Šiai sesijai teisingai nustatyta atsarginė raktų kopija.
+ Ištrinti atsarginę kopiją
+ Atkurti iš atsarginės kopijos
+ Nepavyko gauti naujausios atkūrimo raktų versijos (%s).
+
+ %d naujas raktas buvo pridėtas prie šios sesijos.
+ %d nauji raktai buvo pridėti prie šios sesijos.
+ %d naujų raktų buvo pridėta prie šios sesijos.
+
+
+ Atkurta atsarginė kopija su %d raktu.
+ Atkurta atsarginė kopija su %d raktais.
+ Atkurta atsarginė kopija su %d raktų.
+
+ Atkurta atsarginė kopija %s !
+ Atsarginės kopijos nepavyko iššifruoti naudojant šį atkūrimo raktą: patikrinkite, ar įvedėte teisingą atkūrimo raktą.
+ Įveskite atkūrimo raktą
+ Atrakinti istoriją
+ Importuojami raktai…
+ Atsisiunčiami raktai…
+ Apskaičiuojame atkūrimo raktą…
+ Atkuriama atsarginė kopija:
+ Atsarginės kopijos nepavyko iššifruoti naudojant šią slaptafrazę: patikrinkite, ar įvedėte teisingą atkūrimo slaptafrazę.
+ Pametėte atkūrimo raktą\? Galite nustatyti naują nustatymuose.
+ Įvesti atkūrimo raktą
+ Naudoti atkūrimo raktą, kad atrakinti užšifruotų žinučių istoriją
+ Nežinote savo atkūrimo slaptafrazės, galite %s.
+ naudokite savo atkūrimo raktą
+ Naudokite atkūrimo slaptafrazę, kad atrakintumėte užšifruotų žinučių istoriją
+ Gauname atsarginę versiją…
+ Jei atsijungsite arba prarasite šį prietaisą, galite prarasti prieigą prie savo žinučių.
+ Ar esate tikri\?
+ Netikėta klaida
+ Atkūrimo raktas
+ Generuojame atkūrimo raktą naudojant slaptafrazę, šis procesas gali užtrukti kelioliką sekundžių.
+ Bendrinti atkūrimo raktą su…
+ Prašome pasidaryti kopiją
+ Sustabdyti
+ Pakeisti
+ Atrodo, kad jau esate sukūrę atsarginę raktų kopiją iš kitos sesijos. Ar norite ją pakeisti kuriama\?
+ Jūsų namų serveryje jau yra atsarginė kopija
+ Atkūrimo raktas buvo išsaugotas.
+ Įrašyti kaip failą
+ Bendrinti
+ Išsaugoti atkūrimo raktą
+ Padariau kopiją
+ Baigta
+ Atkūrimo raktą laikykite labai saugioje vietoje, pvz., slaptažodžių tvarkyklėje (arba seife)
+ Atkūrimo raktas yra apsauginis tinklas - juo galite atkurti prieigą prie užšifruotų žinučių, jei pamiršite slaptafrazę.
+\nAtkūrimo raktą laikykite labai saugioje vietoje, pvz., slaptažodžių tvarkyklėje (arba seife)
+ Jūsų raktų atsarginė kopija yra kuriama.
+ Sėkmė !
+ (Išplėstinė) Nustatyti su atkūrimo raktu
+ Arba apsaugokite atsarginę kopiją naudodami atkūrimo raktą ir išsaugokite ją saugioje vietoje.
+ Atsarginės kopijos kūrimas
+ Nustatyti slaptafrazę
+ Jūsų namų serveryje išsaugosime šifruotą raktų kopiją. Apsaugokite atsarginę kopiją slaptafraze, kad ji būtų saugi.
+\n
+\nSiekiant maksimalaus saugumo, ji turėtų skirtis nuo jūsų paskyros slaptažodžio.
+ Apsaugokite atsarginę kopiją slaptafraze.
+ Eksportuoti raktus rankiniu būdu
+ (Išplėstiniai)
+ Pradėti naudoti raktų atsarginį kopijavimą
+ Užšifruotuose kambariuose siunčiamos žinutės yra apsaugotos šifravimu nuo galo iki galo. Tik jūs ir gavėjas (-ai) turite raktus, kad galėtumėte perskaityti šias žinutes.
+\n
+\nSaugiai kurkite atsargines raktų kopijas, kad jų neprarastumėte.
+ Niekada nepraraskite užšifruotų žinučių
+ Ištrinkite slaptafrazę, jei norite, kad ${app_name} sugeneruotų atkūrimo raktą.
+ Slaptafrazė yra per silpna
+ Įveskite slaptafrazę
+ Slaptafrazė nesutampa
+ Įvesti slaptafrazę
+ Patvirtinti slaptafrazę
+ Sukurti slaptafrazę
+ Nerastas galiojantis Google Play Paslaugų APK. Pranešimai gali neveikti tinkamai.
+ +%d
+ %1$s: %2$s
+ suskleisti
+ išplėsti
+ Atsiprašome, įvyko klaida
+ Jei norite toliau naudotis šia paslauga, prašome %s.
+ Prašome %s kad padidinti šią ribą.
+ Šis namų serveris pasiekė savo mėnesio aktyviųjų naudotojų limitą.
+ Šis namų serveris pasiekė mėnesio aktyviųjų naudotojų limitą, todėl kai kurie naudotojai negalės prisijungti.
+ Šis namų serveris viršijo vieną iš savo išteklių limitų.
+ Šis namų serveris viršijo vieną iš savo išteklių limitų, todėl kai kurie naudotojai negalės prisijungti.
+ kreipkitės į savo paslaugų administratorių
+ Spustelėkite čia, kad pamatytumėte senesnes žinutes
+ Šis kambarys yra kito pokalbio tęsinys
+ Pokalbis tęsiamas čia
+ Šis kambarys buvo pakeistas ir nebėra aktyvus.
+ Įveskite savo slaptažodį.
+ Įveskite naudotojo vardą.
+ Deaktyvuoti paskyrą
+ Prašau pamiršti visas mano išsiųstas žinutes, kai mano paskyra bus deaktyvuota (Įspėjimas: dėl to būsimi naudotojai matys nepilną pokalbių vaizdą)
+ Dėl to jūsų paskyra visam laikui taps netinkama naudoti. Negalėsite prisijungti ir niekas negalės iš naujo užregistruoti to paties naudotojo ID. Dėl to jūsų paskyra išeis iš visų kambarių, kuriuose dalyvauja, ir iš jūsų tapatybės serverio bus pašalinti jūsų paskyros duomenys. Šis veiksmas yra negrįžtamas.
+\n
+\nDeaktyvavus paskyrą pagal numatytuosius nustatymus nepamirštame jūsų išsiųstų žinučių. Jei norite, kad pamirštume jūsų žinutes, pažymėkite toliau esantį langelį.
+\n
+\nŽinučių matomumas Matrix sistemoje yra panašus į el. pašto matomumą. Mūsų jūsų žinučių užmiršimas reiškia, kad jūsų išsiųstomis žinutėmis nebus dalijamasi su jokiais naujais ar neregistruotais naudotojais, tačiau registruoti naudotojai, kurie jau turi prieigą prie šių žinučių, vis tiek turės prieigą prie jų kopijos.
+ Deaktyvuoti paskyrą
+ Peržiūrėti dabar
+ Norėdami toliau naudoti %1$s namų serverį, turite peržiūrėti ir sutikti su nuostatomis ir sąlygomis.
+ Avataras
+ Priežastis: %1$s
+ %2$s jus užblokavo iš %1$s
+ %2$s jus pašalino iš %1$s
+ Pakviestas
+ Kambariai
+ Pradžia
+ Sukurti
+ Šifruota žinutė
+ Triukšmingas
+ Tylus
+ Jūs neturite leidimo tai daryti šiame kambaryje.
+ Jūs nesate šiame kambaryje.
+ Pridėti Matrix programėlių
+ Trūksta reikalingo parametro.
+ Kambarys %s nėra matomas.
+ Blokuoti visus
+ Leisti
+ Kambario ID
+ Naudoti mikrofoną
+ Naudoti kamerą
+ Tvarkyti integracijas
+ Jūsų nepatvirtinta sesija \'%s\' prašo šifravimo raktų.
+ Nauja sesija prašo šifravimo raktų.
+\nSesijos pavadinimas: %1$s
+\nPaskutinį kartą matyta: %2$s
+\nJei neprisijungėte prie kitos sesijos, ignoruokite šią užklausą.
+ Pridėjote naują sesiją \'%s\', kuri prašo šifravimo raktų.
+ Nepatvirtinta sesija prašo šifravimo raktų.
+\nSesijos pavadinimas: %1$s
+\nPaskutinį kartą matyta: %2$s
+\nJei neprisijungėte prie kitos sesijos, ignoruokite šią užklausą.
+ Nustatyti naudotojo galios lygį
+ Nustoja ignoruoti naudotoją ir rodo jo žinutes nuo dabar
+ ignoruoja naudotoją, slepiant jo žinutes nuo jūsų
+ Atblokuoja naudotoją su nurodytu id
+ Užblokuoja naudotoją su nurodytu id
+ Rodo veiksmą
+ Prisijungia prie kambario su nurodytu adresu
+ Pakviečia naudotoją su nurodytu id į šį kambarį
+ Nustato kambario pavadinimą
+ Markdown buvo išjungtas.
+ Markdown buvo įjungtas.
+ Rodo informaciją apie naudotoją
+ Pakeičia šio kambario avatarą
+ Pakeičia jūsų rodomą slapyvardį tik šiame kambaryje
+ Pakeičia jūsų avatarą tik šiame kambaryje
+ Pakeičia jūsų rodomą slapyvardį
+ Pašalina naudotoją su nurodytu id iš šio kambario
+ Nustatyti kambario temą
+ Palikti kambarį
+ Neatpažinta komanda: %s
+ Pradėti patvirtinimą
+ Komandos klaida
+ Ignoruoti
+ Bendrinti
+ Komanda \"%s\" atpažįstama, bet nepalaikoma temose.
+ Komandai \"%s\" reikia daugiau parametrų arba kai kurie parametrai yra neteisingi.
+
+ %d pasirinktas
+ %d pasirinkti
+ %d pasirinktų
+
+ Keisti temą
+ Atnaujinti kambarį
+ Numatyta rolė
+ Neturite leidimo atnaujinti roles, reikalingas įvairioms kambario dalims keisti
+ Pasirinkite roles, reikalingas įvairioms kambario dalims keisti
+ Leidimai
+ Peržiūrėti ir atnaujinti roles, reikalingas įvairioms kambario dalims keisti.
+ Kambario leidimai
+ Sertifikatą priimkite tik tuo atveju, jei serverio administratorius yra paskelbęs antspaudą, atitinkantį pirmiau nurodytą.
+ Sertifikatas pakeistas iš anksčiau patikimo į nepatikimą. Serveris galėjo atnaujinti savo sertifikatą. Kreipkitės į serverio administratorių dėl numatyto antspaudo.
+ Sertifikatas pakeistas iš to, kuriuo pasitikėjo jūsų telefonas. Tai LABAI NEĮPRASTA. Rekomenduojama NEPATVIRTINTI šio naujo sertifikato.
+ Jei serverio administratorius nurodė, kad to tikimasi, įsitikinkite, kad toliau pateiktas antspaudas atitinka jo pateiktą antspaudą.
+ Tai gali reikšti, kad kažkas piktavališkai perima jūsų duomenų srautą arba kad telefonas nepasitiki nuotolinio serverio pateiktu sertifikatu.
+ Nepavyko patvirtinti nuotolinio serverio tapatybės.
+ Antspaudas (%s):
+ Ignoruoti
+ Nepasitikėti
+ Pasitikėti
+
+ %d nauja žinutė
+ %d naujos žinutės
+ %d naujų žinučių
+
+ Šifravimas buvo neteisingai sukonfigūruotas, todėl negalite siųsti žinučių. Spustelėkite, kad atidarytumėte nustatymus.
+ Šifravimas buvo neteisingai sukonfigūruotas, todėl negalite siųsti žinučių. Susisiekite su administratoriumi, kad būtų atkurta galiojanti šifravimo būsena.
+ Jūs neturite leidimo rašyti šiame kambaryje.
+ %1$s, %2$s ir kiti
+ %1$s ir %2$s
+ %1$s & %2$s & kiti rašo…
+ %1$s & %2$s rašo…
+ %s rašo…
+ Atblokavus naudotoją, jis vėl galės prisijungti prie kambario.
+ Užblokavus naudotoją, jis bus pašalintas iš šio kambario ir negalės prisijungti dar kartą.
+ Atblokuoti naudotoją
+ Priežastis užblokavimui
+ Išplėstiniai
+ Kita
+ Ignoruojami naudotojai
+ Naudotojo nustatymai
+ Išvalyti medijos talpyklą
+ Išvalyti talpyklą
+ Saugoti mediją
+ Privatumo politika
+ Autorinės teisės
+ Trečiųjų šalių pastabos
+ Terminai ir sąlygos
+
+ %d sekundė
+ %d sekundės
+ %d sekundžių
+
+ Uždelsimas tarp kiekvieno sinchronizavimo
+ Sinchronizavimo užklausos laiko limitas
+ Paleisti sistemos paleidimo metu
+ Kai programėlė yra fone, apie gautus pranešimus nebūsite informuojami.
+ Nėra foninio sinchronizavimo
+ ${app_name} sinchronizuosis fone periodiškai tiksliai nustatytu laiku (galima konfigūruoti).
+\nTai turės įtakos radijo ryšio ir baterijos naudojimui, bus rodomas nuolatinis pranešimas, kad ${app_name} klausosi įvykių.
+ Optimizuotas realiajam laikui
+ ${app_name} bus sinchronizuojama fone taip, kad būtų tausojami riboti įrenginio ištekliai (akumuliatorius).
+\nPriklausomai nuo įrenginio išteklių būklės, operacinė sistema gali atidėti sinchronizavimą.
+ Optimizuotas akumuliatoriui
+ Foninio sinchronizavimo režimas
+ Foninis sinchronizavimas
+ Mobiliuosiuose įrenginiuose negausite pranešimų apie užšifruotuose kambariuose esančius paminėjimus ir raktažodžius.
+ Kambario atnaujinimai
+ Boto žinutės
+ Kvietimai skambinti
+ Kvietimai į kambarį
+ Raktažodžiai
+ \@kambarys
+ Šifruotos grupių žinutės
+ Grupių žinutės
+ Šifruotos tiesioginės žinutės
+ Tiesioginės žinutės
+ Mano naudotojo vardas
+ Mano rodomas vardas
+ Žinutės, kuriose yra @room
+ Boto išsiųstos žinutės
+ Kai mane pakviečia į kambarį
+ Žinutės grupiniuose pokalbiuose
+ Žinutės pokalbiuose vienas su vienu
+ Žinutės, kuriose yra mano naudotojo vardas
+ Žinutės, kuriose yra mano rodomas vardas
+ Kai kambariai atnaujinami
+ Šifruotos žinutės grupiniuose pokalbiuose
+ Šifruotos žinutės pokalbiuose vienas su vienu
+ Pasirinkti LED spalvą, vibraciją, garsą…
+ Tyliųjų pranešimų konfigūravimas
+ Skambučių pranešimų konfigūravimas
+ Triukšmingų pranešimų konfigūravimas
+ Įjungti pranešimus šiai sesijai
+ Įjungti pranešimus šiai paskyrai
+ Pranešimo garsas
+ Ignoruoti optimizavimą
+ Jei naudotojas kurį laiką palieka prietaisą atjungtą nuo elektros tinklo ir nejudantį, su išjungtu ekranu, prietaisas įjungia \"Doze\" režimą. Tai neleidžia programoms prisijungti prie tinklo ir atideda jų darbus, sinchronizavimą ir standartinius žadintuvus.
+ Akumuliatoriaus optimizavimas neįtakoja ${app_name}.
+ Akumuliatoriaus optimizavimas
+ Išjungti apribojimus
+ Įjungti foniniai apribojimai ${app_name}.
+\nDarbas, kurį programa bando atlikti, bus agresyviai ribojamas, kol ji yra fone, ir tai gali turėti įtakos pranešimams.
+\n%1$s
+ Fono apribojimai išjungti ${app_name}. Šis testas turėtų būti atliekamas naudojant mobiliuosius duomenis (be WIFI).
+\n%1$s
+ Patikrinti fono apribojimus
+ Įjungti paleidimą sistemos paleidimo metu
+ Paslauga nebus paleista iš naujo paleidus įrenginį, pranešimų negausite, kol vieną kartą nebus atidaryta ${app_name}.
+ Paslauga bus paleista iš naujo paleidus įrenginį.
+ Paleisti sistemos paleidimo metu
+ Pranešimas buvo paspaustas!
+ Spustelėkite pranešimą. Jei pranešimo nematote, patikrinkite sistemos nustatymus.
+ Pranešimo rodymas
+ Jūs žiūrite pranešimą! Spausk ant manęs!
+ Kai kurie pranešimai yra išjungti pasirinktiniuose nustatymuose.
+ Atkreipkite dėmesį, kad kai kurie pranešimų tipai nustatyti kaip tylūs (pranešimas bus be garso).
+ Pasirinktiniai nustatymai.
+ Šioje sesijoje pranešimai neįjungti.
+\nPatikrinkite ${app_name} nustatymus.
+ Šioje sesijoje pranešimai yra įjungti.
+ Sesijos nustatymai.
+ Pranešimai jūsų paskyroje yra išjungti.
+\nPatikrinkite paskyros nustatymus.
+ Jūsų paskyroje pranešimai yra įjungti.
+ Paskyros nustatymai.
+ Atidaryti nustatymus
+ Sistemos nustatymuose pranešimai yra išjungti.
+\nPatikrinkite sistemos nustatymus.
+ Pranešimai yra įjungti sistemos nustatymuose.
+ Sistemos nustatymai.
+ Vienas ar daugiau testų nepavyko, pateikite pranešimą apie klaidą ir padėkite mums ją ištirti.
+ Vienas ar daugiau testų nepavyko, išbandykite siūlomą (-us) pataisymą (-us).
+ Pagrindinė diagnostika yra gera. Jei vis dar negaunate pranešimų, pateikite pranešimą apie klaidą ir padėkite mums ją ištirti.
+ Vykdoma… (%1$d iš %2$d)
+ Atlikti bandymus
+ Trikčių diagnostika
+ Pranešimų trikčių šalinimas
+ Raktažodžiai negali turėti \'%s\'
+ Raktažodžiai negali prasidėti su \'.\'
+ Pridėti naują raktažodį
+ Jūsų raktažodžiai
+ Praneškite man apie
+ Kita
+ Paminėjimai ir raktažodžiai
+ Numatyti pranešimai
+ Įjungti pranešimus el. paštu %s
+ Norėdami gauti pranešimą el. paštu, susiekite el. paštą su savo Matrix paskyra
+ Pranešimas el. paštu
+ Pranešimo svarba pagal įvykį
+ Išplėstiniai pranešimų nustatymai
+ Įsitikinkite, kad paspaudėte jums atsiųstame el. laiške esančią nuorodą.
+ Pašalinti %s\?
+ Telefono numeriai
+ Prie jūsų paskyros nepridėtas joks el. paštas
+ El. pašto adresai
+ Rodyti programos informaciją sistemos nustatymuose.
+ Programos informacija
+ Pridėti telefono numerį
+ Prie jūsų paskyros nepridėtas joks telefono numeris
+ Pridėti el. pašto adresą
+ Rodomas vardas
+ Profilio nuotrauka
+ Sesija buvo atjungta!
+ %1$s ir %2$s
+ Nėra rezultatų
+ Filtruoti užblokuotus narius
+ Filtruoti kambario narius
+ Ieškoti
+ Kambarys buvo paliktas!
+ Pridėti prie pagrindinio ekrano
+ Nėra
+ Tik paminėjimai & raktažodžiai
+ Visos žinutės
+ Filtruoti temas kambaryje
+ Temos artėja prie beta versijos 🎉
+ Siųsti nuotraukas ir vaizdo įrašus
+ Atidaryti fotoaparatą
+ Rodyti žinučių burbulus
+ Tiesioginė buvimo vieta
+ Bendrinti vietą
+ Norėdami bendrinti tiesioginę buvimo vietą šiame kambaryje, turite turėti tinkamus leidimus.
+ Neturite leidimo bendrinti tiesioginę buvimo vietą
+ Atnaujinta prieš %1$s
+ Laikinas pritaikymas: vietos išlieka kambario istorijoje
+ Įjungti tiesioginį buvimo vietos bendrinimą
+ Bendrinama buvimo vieta
+ ${app_name} tiesioginė buvimo vieta
+ %1$s liko
+ Sustabdyti
+ Tiesiogiai iki %1$s
+ Žiūrėti tiesioginę buvimo vietą
+ Tiesioginė buvimo vieta baigėsi
+ Įkeliama tiesioginė vieta…
+ Tiesioginė buvimo vieta įjungta
+ Nepavyksta įkelti žemėlapio
+\nŠis namų serveris gali būti nesukonfigūruotas rodyti žemėlapius.
+ Nepavyko įkelti žemėlapio
+ Atidaryti per
+ ${app_name} negalėjo pasiekti jūsų vietos. Prašome pabandyti vėliau.
+ ${app_name} negalėjo pasiekti jūsų vietos
+ 8 valandas
+ 1 valandą
+ 15 minučių
+ Bendrinti savo tiesioginę buvimo vietą
+ Bendrinti šią vietą
+ Bendrinti šią vietą
+ Bendrinti tiesioginę buvimo vietą
+ Bendrinti tiesioginę buvimo vietą
+ Bendrinti mano dabartinę vietą
+ Bendrinti mano dabartinę vietą
+ Priartinti esamą vietą
+ Pasirinktos vietos smeigtukas žemėlapyje
+ Žemėlapis
+ Rezultatai atskleidžiami tik tada, kai baigiate apklausą
+ Uždaryta apklausa
+ Balsuotojai mato rezultatus iškart po balsavimo
+ Atidaryti apklausą
+ Apklausos tipas
+ Redaguoti apklausą
+ Ar tikrai norite pašalinti šią apklausą\? Pašalinę ją negalėsite susigrąžinti.
+ Tai neleis žmonėms balsuoti ir bus rodomi galutiniai apklausos rezultatai.
+ Pašalinti apklausą
+ Apklausa baigėsi
+ Prabalsuota
+ Baigti apklausą
+ laimėtojo parinktis
+ Rezultatai bus matomi pasibaigus apklausai
+ Nėra balsų
+ Iš naujo paleiskite programą, kad pakeitimas įsigaliotų.
+ Įjungti LaTeX matematika
+ %s nustatymuose, kad gautumėte kvietimus tiesiogiai į ${app_name}.
+ Kvietimas į šią erdvę buvo išsiųstas į %s, kuris nėra susijęs su jūsų paskyra
+ Kvietimas į šį kambarį buvo išsiųstas į %s, kuris nėra susijęs su jūsų paskyra
+ Atkreipkite dėmesį, kad atnaujinus bus sukurta nauja kambario versija. Visos dabartinės žinutės liks šiame archyvuotame kambaryje.
+ Kiekvienas iš %s galės rasti šį kambarį ir prisijungti prie jo - nereikės visų kviesti rankiniu būdu. Tai galėsite bet kada pakeisti kambario nustatymuose.
+ (%1$s)
+ %1$s (%2$s)
+ Nepavyko paleisti %1$s
+ Pristabdyti %1$s
+ Paleisti %1$s
+ %1$d minutės %2$d sekundės
+ %1$s, %2$s, %3$s
+ %1$ds liko
+ Atsiprašome, bandant prisijungti įvyko klaida: %s
+ Atnaujinti į rekomenduojamą kambario versiją
+ Šiame kambaryje naudojama kambario versija %s, kurią šis namų serveris pažymėjo kaip nestabilią.
+ Norint atnaujinti kambarį, reikia leidimo
+ Jūs atnaujinsite šį kambarį iš %1$s į %2$s.
+ Kambario atnaujinimas yra išplėstinis veiksmas ir paprastai rekomenduojamas, kai kambarys yra nestabilus dėl klaidų, trūkstamų funkcijų ar saugumo spragų.
+\nPaprastai tai turi įtakos tik tam, kaip kambarys apdorojamas serveryje.
+ Atnaujinti privatų kambarį
+ Atnaujinti viešą kambarį
+ Kai kurie kambariai gali būti paslėpti, nes yra privatūs ir į juos reikia pakvietimo.
+ Kai kurie kambariai gali būti paslėpti, nes yra privatūs ir į juos reikia pakvietimo.
+\nJūs neturite leidimo pridėti kambarių.
+ Šioje erdvėje nėra kambarių
+ Dėl papildomos informacijos kreipkitės į savo namų serverio administratorių
+ Atrodo, kad jūsų namų serveris dar nepalaiko erdvių
+ Norite eksperimentuoti\?
+\nĮ erdvę galite įtraukti esamas erdves.
+ Visi kambariai kuriuose esate, bus rodomi pradžioje.
+ Valdyti kambarius ir erdves
+ Pažymėti kaip nesiūlomą
+ Pažymėti kaip siūlomą
+ Siūlomas
+ Valdyti kambarius
+ Ieškote ko nors ne iš %s\?
+ %s kviečia tave
+ Rodyti naujausią profilio informaciją (avatarą ir rodomą vardą) visose žinutėse.
+ Rodyti naujausią naudotojo informaciją
+ Pastaba: programa bus paleista iš naujo
+ Įjungti temų žinutes
+ Jūsų sistema automatiškai išsiųs žurnalus, kai įvyks negalėjimo iššifruoti klaida
+ Automatiškai pranešti apie iššifravimo klaidas.
+ Erdvės - tai naujas kambarių ir žmonių grupavimo būdas.
+ Įtraukite erdvę į bet kurią valdomą erdvę.
+ Pridėti esamas erdves
+ Pridėti esamus kambarius
+ Pridėti esamus kambarius ir erdvę
+ Kai kurie rezultatai gali būti paslėpti, nes jie yra privatūs ir į juos reikia pakvietimo.
+ Rezultatų nerasta
+ Iš temos
+ Patarimas: Ilgai bakstelėkite žinutę ir naudokite “%s”.
+ Temos padeda išlaikyti pokalbių temą ir lengviau juos sekti.
+ Išlaikykite diskusijas organizuotas su temomis
+ Rodo visas temas, kuriose dalyvavote
+ Mano temos
+ Rodo visas temas iš dabartinio kambario
+ Visos temos
+ Filtras
+ Visos temos
+ Tema
+ Keisti leidimus
+ Keisti pagrindinį kambario adresą
+ Keisti kambario avatarą
+ Keisti valdiklius
+ Pranešti visiems
+ Pašalinti kitų išsiųstas žinutes
+ Užblokuoti naudotojus
+ Pašalinti naudotojus
+ Keisti nustatymus
+ Kviesti naudotojus
+ Siųsti žinutes
+ Įjungti kambario šifravimą
+ Keisti kambario pavadinimą
+ Keisti istorijos matomumą
+ %s atnaujino čia.
+
diff --git a/library/ui-strings/src/main/res/values-lv/strings.xml b/library/ui-strings/src/main/res/values-lv/strings.xml
index f1fa1502c1..1787653fae 100644
--- a/library/ui-strings/src/main/res/values-lv/strings.xml
+++ b/library/ui-strings/src/main/res/values-lv/strings.xml
@@ -469,7 +469,7 @@
TēmaAtšifrēšanas kļūdaIerīces nosaukums
- Sesijas ID
+ Sesijas IDSesijas atslēgaEksportēt istabas šifrēšanas atslēgasEksportēt istabas atslēgas
diff --git a/library/ui-strings/src/main/res/values-nb-rNO/strings.xml b/library/ui-strings/src/main/res/values-nb-rNO/strings.xml
index 031b380c7e..7af718d920 100644
--- a/library/ui-strings/src/main/res/values-nb-rNO/strings.xml
+++ b/library/ui-strings/src/main/res/values-nb-rNO/strings.xml
@@ -119,7 +119,7 @@
Bannlyste brukereAvansertTema
- Økt-ID
+ Økt-IDØktnøkkelEksporterImporter
diff --git a/library/ui-strings/src/main/res/values-nl/strings.xml b/library/ui-strings/src/main/res/values-nl/strings.xml
index b1d239963e..ce122b0646 100644
--- a/library/ui-strings/src/main/res/values-nl/strings.xml
+++ b/library/ui-strings/src/main/res/values-nl/strings.xml
@@ -3,10 +3,10 @@
Uitnodiging van %s%1$s heeft %2$s uitgenodigd%1$s heeft u uitgenodigd
- %1$s neemt nu deel aan het gesprek
- %1$s heeft het gesprek verlaten
+ %1$s is deelnemer geworden van de kamer
+ %1$s heeft het de kamer verlaten%1$s heeft de uitnodiging geweigerd
- %1$s heeft %2$s uit het gesprek verwijderd
+ %1$s heeft %2$s verwijderd%1$s heeft %2$s ontbannen%1$s heeft %2$s verbannen%1$s heeft de uitnodiging van %2$s ingetrokken
@@ -15,20 +15,20 @@
%1$s heeft zijn/haar naam aangepast van %2$s naar %3$s%1$s heeft zijn/haar naam verwijderd (%2$s)%1$s heeft het onderwerp veranderd naar: %2$s
- %1$s heeft de gespreksnaam veranderd naar: %2$s
+ %1$s heeft de kamernaam veranderd naar: %2$s%s heeft een video-oproep gemaakt.%s heeft een spraakoproep gemaakt.%s heeft de oproep beantwoord.%s heeft de oproep beëindigd.
- %1$s heeft de toekomstige gespreksgeschiedenis zichtbaar gemaakt voor %2$s
- alle deelnemers aan het gesprek, vanaf het punt dat ze zijn uitgenodigd.
- alle deelnemers aan het gesprek, vanaf het punt dat ze zijn toegetreden.
- alle deelnemers aan het gesprek.
+ %1$s heeft de toekomstige kamergeschiedenis zichtbaar gemaakt voor %2$s
+ alle kamerdeelnemers, vanaf het punt dat ze zijn uitgenodigd.
+ alle kamerdeelnemers, vanaf het punt dat ze deelnemer zijn geworden.
+ alle kamerdeelnemers.iedereen.(avatar is ook veranderd)
- %1$s heeft de gespreksnaam verwijderd
- %1$s heeft het gespreksonderwerp verwijderd
- %1$s heeft een uitnodiging naar %2$s gestuurd om het gesprek toe te treden
+ %1$s heeft de kamernaam verwijderd
+ %1$s heeft het kameronderwerp verwijderd
+ %1$s heeft een uitnodiging naar %2$s gestuurd om deelnemer te worden van de kamer%1$s heeft de uitnodiging voor %2$s aanvaard** Kan niet ontsleutelen: %s **Het apparaat van de afzender heeft geen sleutels voor dit bericht gestuurd.
@@ -36,27 +36,27 @@
Matrix-foutE-mailadresTelefoonnummer
- Gespreksuitnodiging
+ Kameruitnodiging%1$s en %2$s
- Leeg gesprek
+ Lege kamerInitiële synchronisatie:
\nAccount wordt geïmporteerd…Initiële synchronisatie:
\nCrypto wordt geïmporteerdInitiële synchronisatie:
-\nGesprekken worden geïmporteerd
+\nKamers importeren
Initiële synchronisatie:
-\nDeelgenomen gesprekken worden geïmporteerd
-\nDit kan enige tijd in beslag nemen
+\nGesprekken worden geladen
+\nAls u aan veel kamers deelneemt kan dit even duren
Initiële synchronisatie:
-\nUitgenodigde gesprekken worden geïmporteerd
+\nUitgenodigde kamers worden geïmporteerd
Initiële synchronisatie:
-\nVerlaten gesprekken worden geïmporteerd
+\nVerlaten kamers worden geïmporteerd
Initiële synchronisatie:
\nAccountgegevens worden geïmporteerd
- %s heeft dit gesprek geüpgraded.
+ %s heeft deze kamer geüpgraded.Bericht wordt verstuurd…
- %1$s heeft de uitnodiging voor %2$s om het gesprek toe te treden ingetrokken
+ %1$s heeft de uitnodiging voor %2$s om deelnemer te worden van de kamer ingetrokkenUitnodiging van %1$s. Reden: %2$s%1$s heeft %2$s uitgenodigd. Reden: %3$s%1$s heeft u uitgenodigd. Reden: %2$s
@@ -69,8 +69,8 @@
%1$s heeft de uitnodiging voor %2$s aanvaard. Reden: %3$s%1$s heeft de uitnodiging van %2$s ingetrokken. Reden: %3$s
- %1$s heeft %2$s als gespreksadres toegevoegd.
- %1$s heeft %2$s als gespreksadressen toegevoegd.
+ %1$s heeft %2$s als kameradres toegevoegd.
+ %1$s heeft %2$s als kameradressen toegevoegd.%1$s heeft %2$s als gespreksadres verwijderd.
@@ -123,7 +123,7 @@
Logboek versturenCrash-logboek versturenSchermafdruk versturen
- Fout melden
+ Probleem meldenBeschrijf de fout. Wat heeft u gedaan\? Wat verwachtte u dat er zou gebeuren\? Wat is er echt gebeurd\?Beschrijf hier uw probleemOm het probleem te kunnen onderzoeken worden logboeken van deze cliënt met de foutmelding verstuurd. Deze foutmelding, inclusief de logboeken en schermafdruk, zullen niet openbaar zichtbaar zijn. Indien u liever alleen de bovenstaande tekst verstuurt, haal dan het vinkje weg:
@@ -132,7 +132,7 @@
Versturen van foutmelding is mislukt (%s)Voortgang (%s%%)De toepassing is de vorige keer gecrasht. Wilt u dit melden\?
- Gesprek toetreden
+ Deelnemen aan kamerInlognaamAfmeldenServer-URL
@@ -172,9 +172,9 @@
NEEVerdergaanVerwijderen
- Toetreden
+ DeelnemenAfwijzen
- Ga naar ongelezen
+ Naar ongelezen springenGesprek verlatenWeet u zeker dat u het gesprek wilt verlaten\?TWEEGESPREKKEN
@@ -210,7 +210,7 @@
Telefoonnummer toevoegenToon informatie over de app in de systeeminstellingen.App-informatie
- Meldingen voor deze account inschakelen
+ Meldingen voor dit account inschakelenMeldingen voor deze sessie inschakelenBerichten in één-op-één-gesprekkenBerichten in groepsgesprekken
@@ -227,7 +227,7 @@
CopyrightPrivacybeleidCache wissen
- Persoonsinstellingen
+ GebruikersinstellingenMeldingenGenegeerde personenOverige
@@ -265,7 +265,7 @@
IedereenAlleen deelnemers (vanaf het moment dat deze optie wordt geselecteerd)Alleen deelnemers (vanaf het moment dat ze worden uitgenodigd)
- Alleen deelnemers (vanaf het moment dat ze toetreden)
+ Alleen deelnemers (vanaf het moment dat ze deelnemer zijn geworden)Verbannen personenGeavanceerdInterne ID van dit gesprek
@@ -275,7 +275,7 @@
Niet instellen als hoofdadresOntsleutelingsfoutPublieke naam
- Sessie ID
+ Sessie-IDSessiesleutelE2E-gesprekssleutels exporterenGesprekssleutels exporteren
@@ -294,7 +294,7 @@
VerifiërenOm te verifiëren dat deze sessie vertrouwd kan worden, contacteert u de eigenaar via een andere methode (bv. persoonlijk of via een telefoontje) en vraagt u hem/haar of de sleutel die hij/zij ziet in zijn/haar persoonsinstellingen van deze sessie overeenkomt met de sleutel hieronder:Als het overeenkomt, drukt u op de knop ‘Verifiëren’ hieronder. Als het niet overeenkomt, dan onderschept iemand anders deze sessie en zou u het beter blokkeren. In de toekomst zal dit verificatieproces verbeterd worden.
- Kies een gesprekscatalogus
+ Kamermap kiezenServernaamAlle gesprekken op server %sAlle lokale gesprekken op %s
@@ -334,7 +334,7 @@
user_id ontbreekt in het verzoek.Gesprek %s is niet zichtbaar.Matrix-apps toevoegen
- Geluidsmeldingen
+ Belangrijke meldingenStille meldingenFoutmeldingFoto maken
@@ -423,7 +423,7 @@
Voer uw wachtwoord in.Beschrijf het probleem in het Engels, indien mogelijk.Media bekijken vóór het versturen
- Toont een actie
+ Geeft activiteit weerVerbant persoon met gegeven IDHeft verbanning van persoon met gegeven ID opStel het machtsniveau van een persoon in
@@ -456,7 +456,7 @@
AanvaardenGelieve het beleid van deze server te lezen en aanvaarden:Oproepen
- Gebruik de standaardbeltoon van ${app_name} voor inkomende oproepen
+ Standaardbeltoon van ${app_name} gebruiken voor inkomende oproepenBeltoon voor inkomende oproepenSelecteer beltoon voor oproepen:Eruit sturen
@@ -551,7 +551,7 @@
${app_name} wordt niet beperkt door accuoptimalisatie.Als een persoon een apparaat los van de oplader een tijd laat stilliggen, met het scherm uitgeschakeld, gaat het apparaat in slaapmodus. Dit verhindert apps de toegang tot het netwerk, en stelt hun taken, synchronisaties en standaardalarmen uit.Optimalisatie negeren
- Lawaaiierige meldingen configureren
+ Belangrijke meldingen configurerenOproepmeldingen configurerenStille meldingen configurerenBepaal de LED-kleur, vibratie, geluid, …
@@ -722,7 +722,7 @@
Gebruik een integratiebeheerder om bots, bruggen, widgets en stickerpakketten te beheren.
\nIntegratiebeheerders ontvangen configuratiedata en kunnen widgets aanpassen, gespreksuitnodigingen versturen en bestuursniveaus instellen namens u.Ontdekken
- Beheer uw ontdekinstellingen.
+ Beheer uw ontdekkingsinstellingen.Integraties toestaanIntegratiebeheerderWidget
@@ -774,18 +774,18 @@
Gebeurtenis verwijderd door persoonGebeurtenis gemodereerd door gesprek beheerderNiet correcte gebeurtenis, kan niet weergeven
- Maak een nieuw gesprek aan
+ Nieuwe kamer aanmakenGeen netwerk. Controleer uw internet verbinding.Wijzigen
- Wijzig netwerk
+ Netwerk wijzigenEven wachten…
- Dit gesprek kan niet worden voorvertoond
+ Deze kamer kan niet worden voorvertoondGesprekkenDirecte BerichtenAANMAKEN
- Gespreksnaam
+ NaamPubliek
- Iedereen kan deze kamer kunnen toetreden
+ Iedereen kan deelnemer worden van deze kamerAfspelenU heeft het hoofdadres voor dit gesprek verwijderd.U heeft %1$s uitgenodigd. Reden: %2$s
@@ -861,8 +861,8 @@
U heeft een audiogesprek geopend.U heeft een videogesprek geopend.U heeft de kamernaam veranderd naar: %1$s
- U heeft de kamer afbeelding aangepast
- %1$s heeft de kamer afbeelding aangepast
+ U heeft de kamerafbeelding aangepast
+ %1$s heeft de kamerafbeelding aangepastU heeft het onderwerp gewijzigd naar: %1$sU heeft uw weergavenaam verwijderd (voorheen %1$s)U heeft de uitnodiging van %1$s ingetrokken
@@ -891,8 +891,8 @@
U heeft %1$s als gespreksadressen verwijderd.
- U heeft %1$s als gespreksadres toegevoegd.
- U heeft %1$s als gespreksadressen toegevoegd.
+ U heeft %1$s als kameradres toegevoegd.
+ U heeft %1$s als kameradressen toegevoegd.U heeft de uitnodiging van %1$s ingetrokken. Reden: %2$sU heeft de uitnodiging voor %1$s aanvaard. Reden: %2$s
@@ -913,12 +913,12 @@
• Servers die overeenkomen met %s zijn verbannen.U heeft hier geüpgraded.%s heeft hier geüpgraded.
- U heeft toekomstige gespreksgeschiedenis zichtbaar gemaakt voor %1$s
+ U heeft toekomstige kamergeschiedenis zichtbaar gemaakt voor %1$s%1$ds over%s is toegetreden.Conclusie BevestigingKamerinstellingen
- Gespreksnaam
+ KamernaamIntegraties Beheren%d uitnodiging
@@ -940,7 +940,7 @@
PIN BevestigenUitnodiging intrekkenContactpersonen
- Gespreksnaam
+ KamernaamBeveiligingszinInstellenBeveiligde backup
@@ -962,7 +962,7 @@
SleutelverzoekenVerwijderen BevestigenAccountgegevens
- Ontwikkel Gereedschap
+ OntwikkelaarsgereedschapQR-codeSleutels herstellenGekruist Ondertekenen Initialiseren
@@ -971,7 +971,7 @@
Actieve SessiesVersleuteling inschakelenVersleuteling inschakelen\?
- Berichtverwerker
+ BerichtbewerkerGesprek VerlatenEén persoon
@@ -1043,7 +1043,7 @@
Directe BerichtenFeedbackToken registreren
- Pushregels
+ Push-regelsBericht verwijderdBeveiligde BackupActieve widgets
@@ -1061,7 +1061,7 @@
SSL-fout.Camera wisselenDraadloze Koptelefoon
- Spaces
+ RuimtenWisselenOpwaarderenAanbevolen
@@ -1187,7 +1187,7 @@
KopiërenGeef toestemming om de camera te gebruiken via de systeeminstellingen om deze actie uit te voeren.Sommige rechten ontbreken om deze actie uit te voeren, geeft a.u.b. toestemming via de systeeminstellingen.
- Spaces
+ RuimtenBegin met chattenHerstellenAfwijzen
@@ -1224,13 +1224,13 @@
Aan de slagSpacerechtenGespreksrechten
- Door deze persoon niet meer de verbannen kan hij/zij opnieuw toetreden tot de space.
- Door deze persoon niet meer de verbannen kan hij/zij opnieuw toetreden tot het gesprek.
+ Door de verbanning op te heffen kan deze gebruiker opnieuw deelnemer worden van de ruimte.
+ Door de verbanning op te heffen kan deze gebruiker opnieuw deelnemer worden van de kamer.Door deze persoon te verbannen zal hij/zij verwijderd worden uit deze space en voorkomen dat hij/zij opnieuw toetreedt.Reden voor verbanning
- Door deze persoon de verwijderen zal hij/zij niet meer in deze space zitten.
+ De gebruiker zal worden verwijderd uit deze ruimte.
\n
-\nOm te voorkomen dat hij/zij opnieuw toetreedt, kunt u hem/haar ook verbannen.
+\nOm te voorkomen dat ze opnieuw toetreden, kunt u ze verbannen.Door deze persoon te verwijderen zal hij/zij niet meer in dit gesprek zitten.
\n
\nOm te voorkomen dat hij/zij opnieuw toetreedt, kun je hem/haar ook verbannen.
@@ -1241,7 +1241,7 @@
\n
\nU kunt deze actie op elk moment ongedaan maken in de algemene instellingen.
U kunt deze wijziging niet ongedaan maken omdat uzelf degradeert, als u de laatste persoon met rechten bent in het gesprek zal het onmogelijk zijn om opnieuw rechten te krijgen.
- Dit gesprek is niet publiek. U kunt niet opnieuw toetreden zonder uitnodiging.
+ Deze kamer is niet publiek. U kunt niet opnieuw deelnemer worden zonder uitnodiging.Toegang verlenen tot uw contactpersonen.Om de QR-code te scannen moet u toegang verlenen tot de camera.Oproep beëindigen…
@@ -1353,7 +1353,7 @@
Toevoegen aan lage prioriteitVerwijder van favorietenToevoegen aan favorieten
- Alle berichten (luidruchtig)
+ Alle belangrijke berichtenDeze inhoud is als ongepast gerapporteerd.
\n
\nAls u geen inhoud van deze persoon meer wilt zien, kunt u deze negeren om hun berichten te verbergen.
@@ -1396,7 +1396,7 @@
Het lijkt erop dat de server er te lang over doet om te reageren. Dit kan worden veroorzaakt door een slechte verbinding of een fout met de server. Probeer het over een tijdje opnieuw.Probeer het opnieuw zodra u de algemene voorwaarden van uw homeserver hebt geaccepteerd.Uitgebreide logboeken helpen ontwikkelaars door meer logboeken te verstrekken wanneer u een RageShake verzendt. Zelfs wanneer ingeschakeld, registreert de toepassing geen berichtinhoud of andere privégegevens.
- Uitgebreide logboeken inschakelen.
+ Uitgebreide logboeken inschakelenGa akkoord met de servicevoorwaarden van de identiteitsserver (%s), zodat u vindbaar bent op e-mailadres of telefoonnummer.U deelt momenteel e-mailadressen of telefoonnummers op de identiteitsserver %1$s. U moet opnieuw verbinding maken met %2$s om ze niet meer te delen.De verificatiecode is niet correct.
@@ -1411,7 +1411,7 @@
Stuur e-mailadressen en telefoonnummers naar %sToestemming gevenMijn toestemming intrekken
- Uw server-beleid
+ Uw thuisserverbeleidKan geen server bereiken op de URL %s. Controleer uw link of kies handmatig een server.Uw contacten zijn privé. Om personen van uw contacten te ontdekken, hebben we uw toestemming nodig om contactgegevens naar uw identiteitsserver te sturen.We hebben u een bevestigingsmail gestuurd naar %s, controleer eerst uw e-mail en klik op de bevestigingslink
@@ -1467,8 +1467,8 @@
Afbeelding comprimeren…Bestand versturen (%1$s / %2$s)Miniatuur versturen (%1$s / %2$s)
- Toon volledige geschiedenis in versleutelde kamers
- Toon verborgen gebeurtenissen op de tijdlijn
+ Volledige geschiedenis in versleutelde kamers weergeven
+ Verborgen gebeurtenissen op de tijdlijn weergevenGeef feedbackDe feedback kan niet worden verzonden (%s)Bedankt, uw feedback is succesvol verzonden
@@ -1479,8 +1479,8 @@
Bedankt, de suggestie is succesvol verzondenBeschrijf hier uw suggestieSchrijf hieronder uw suggestie.
- Doe een suggestie
- Systeem instellingen
+ Een voorstel doen
+ SysteeminstellingenVersiesHulp bij het gebruik van ${app_name}Hulp en ondersteuning
@@ -1500,9 +1500,9 @@
\n
\n%s
Kameronderwerp (optioneel)
- Aanmaken nieuwe Space
- Toon een aanduiding voor verwijderde berichten
- Toon verwijderde berichten
+ Nieuwe ruimte aanmaken
+ Geeft een plaatsvervangende melding weer voor verwijderde berichten.
+ Verwijderde berichten weergevenBeveiligde back-up instellenBeveiliging tegen verlies van toegang tot versleutelde berichten en gegevensDe herstelsleutel is opgeslagen.
@@ -1565,7 +1565,7 @@
Gepubliceerde adressen kunnen door iedereen op elke server worden gebruikt om lid te worden van uw kamer. Om een adres te publiceren, moet het eerst als lokaal adres worden ingesteld.Gepubliceerde adressenAdressen van deze kamer bekijken en beheren.
- Spaceadressen
+ Ruimte-adressenBekijk en beheer de adressen van deze kamer en de zichtbaarheid ervan in de kamerdirectory.KameradressenSta toe om gasten te laten deelnemen
@@ -1584,7 +1584,7 @@
Deze server biedt geen beleid.Bibliotheken van derdenUw identiteitsserverbeleid
- ${app_name} beleid
+ ${app_name}-beleidWe delen geen informatie met derdenWe registreren of profileren geen accountgegevenshier
@@ -1601,7 +1601,7 @@
Voeg een knop toe aan de invoerveld om het emoji-toetsenbord te openenEmoji-toetsenbord weergevenGebruik /confetti commando of stuur een bericht met ❄️ of 🎉
- Toon chateffecten
+ Chateffecten weergevenKamer upgradesBerichten door botKameruitnodigingen
@@ -1754,7 +1754,7 @@
Pincode is vereist na 2 minuten ${app_name} niet te hebben gebruikt.Pincode vereist na 2 minutenGeef alleen het aantal ongelezen berichten weer in een eenvoudige melding.
- Toon details zoals kamernamen en berichtinhoud.
+ Geeft details weer zoals kamernamen en berichtinhoud.Inhoud in meldingen weergevenPincode is de enige manier om ${app_name} te ontgrendelen.Schakel apparaatspecifieke biometrische gegevens in, zoals vingerafdrukken en gezichtsherkenning.
@@ -1831,8 +1831,8 @@
Andere beschikbare talenDeel deze code met mensen zodat ze deze kunnen scannen om u toe te voegen en te beginnen met chatten.Mijn code
- Deel mijn code
- Scan een QR-code
+ Mijn code delen
+ Een QR-code scannenWe kunnen geen personen uitnodigen. Controleer de personen die u wilt uitnodigen en probeer het opnieuw.Uitnodigingen verzonden naar %1$s en nog één
@@ -1843,7 +1843,7 @@
Uitnodiging verzonden naar %1$s🔐️ Doe mee met ${app_name}Hé, praat met me op ${app_name}: %s
- Nodig vrienden uit
+ Vrienden uitnodigenMensen toevoegenWe kunnen je DM niet maken. Controleer de personen die u wilt uitnodigen en probeer het opnieuw.De link %1$s brengt u naar een andere site: %2$s.
@@ -1912,7 +1912,7 @@
Kan sleutels niet importerenWachten op %s…Bijna daar! Op bevestiging wachten…
- Bijna daar! Toont het andere apparaat een vinkje\?
+ Bijna klaar! Toont het andere apparaat een vinkje\?Een onderwerp toevoegen%s om mensen te laten weten waar deze kamer over gaat.Dit is het begin van uw privéberichtgeschiedenis met %s.
@@ -1989,7 +1989,7 @@
\nWees voorzichtig, het kan leiden tot onverwacht gedrag.Vliegtuigmodus is ingeschakeldVerbinding met de server is verbroken
- Bijna daar! Toont %s een vinkje\?
+ Bijna klaar! Toont %s een vinkje\?Totdat deze persoon deze sessie vertrouwt, worden berichten die van en naar de sessie worden verzonden, gelabeld met waarschuwingen. U kunt het ook handmatig verifiëren.%1$s (%2$s) aangemeld met een nieuwe sessie:Deze sessie wordt vertrouwd voor veilig berichtenverkeer omdat %1$s (%2$s) deze heeft geverifieerd:
@@ -2011,7 +2011,7 @@
ServerversieServer naamAfmelden voor deze sessie
- Toon alle sessies
+ Alle sessies tonenUw serverbeheerder heeft standaard end-to-end versleuteling uitgeschakeld in privékamers en privéberichten.Kruisondertekenen is niet ingeschakeldKruisondertekenen is ingeschakeld.
@@ -2070,21 +2070,21 @@
Ze komen niet overeenNiet-vertrouwd inloggenUw e-maildomein is niet geautoriseerd om op deze server te registreren
- Space aanmaken…
+ Ruimte aanmaken…Kamer aanmaken…Sommige tekens zijn niet toegestaanGeef een kameradres opDit adres is al in gebruik
- Space adres
+ Ruimte-adresU kunt dit inschakelen als de kamer alleen wordt gebruikt voor samenwerking met interne teams op uw server. Dit kan later niet meer worden gewijzigd.Blokkeer iedereen die geen deel uitmaakt van %s om ooit deel te nemen aan deze kamerVerberg geavanceerd
- Toon geavanceerd
+ Geavanceerd weergevenEenmaal ingeschakeld, kan versleuteling niet worden uitgeschakeld.Voegt ( ͡° ͜ʖ ͡°) toe aan een bericht in platte tekstVoegt ¯\\_(ツ)_/¯ toe aan een bericht in platte tekst
- Toon wat nuttige informatie om te helpen bij het debuggen van de applicatie
- Toon debug-informatie op het scherm
+ Geeft wat nuttige informatie weer om te helpen bij foutopsporing van de app.
+ Foutopsporingsinformatie op het scherm weergeven${app_name} kan vaker crashen als er een onverwachte fout optreedtLaat alleen de eerste resultaten zien, typ meer letters…Schud je telefoon om de detectiedrempel te testen
@@ -2157,9 +2157,9 @@
Ik heb mijn e-mailadres geverifieerdTik op de link om uw nieuwe wachtwoord te bevestigen. Klik hieronder als u de link hebt gevolgd die erin staat.Word eigenaar van uw gesprekken.
- Space aanmaken
- Space aanmaken…
- Aanmaken een space
+ Ruimte aanmaken
+ Ruimte aanmaken…
+ Een ruimte aanmakenGebeurtenis inhoudHoud er rekening mee dat bij het upgraden een nieuwe versie van de kamer wordt gemaakt. Alle huidige berichten blijven in deze gearchiveerde kamer.Iedereen in een ouderkamer kan deze kamer vinden en er lid van worden. Het is niet nodig om iedereen handmatig uit te nodigen. U kunt dit op elk moment wijzigen in de kamer instellingen.
@@ -2247,7 +2247,7 @@
Experimenteel voelen\?
\nU kunt bestaande spaces aan een space toevoegen.Alle kamers waarin u deelneemt, worden weergegeven in Home.
- Toon alle kamers in Home
+ Alle kamers op startscherm weergevenKamers en spaces beherenMarkeren als aanbevolenMarkeren als niet aanbevolen
@@ -2300,16 +2300,16 @@
Voeg wat details toe om het te laten opvallen. U kunt deze op elk moment wijzigen.Alleen op uitnodiging, het beste voor uzelf of teamsOpen voor iedereen, het beste voor gemeenschappen
- Een privé space voor u en uw teamgenoten
+ Een privé-ruimte voor u en uw teamgenotenIk en teamgenotenEen privé space om je kamers te organiserenAlleen ikZorg ervoor dat de juiste mensen toegang hebben tot %s.Met wie werkt u samen\?U kunt dit later wijzigen
- Wat voor soort space wilt u aanmaken\?
- Uw privé space
- Uw openbare space
+ Wat voor soort ruimte wilt u aanmaken\?
+ Uw privé-ruimte
+ Uw openbare ruimteSpace toevoegenPrivé spaceOpenbare space
@@ -2352,7 +2352,7 @@
LocatieDe versleuteling is verkeerd geconfigureerd, zodat u geen berichten kunt versturen. Klik om instellingen te openen.De versleuteling is verkeerd geconfigureerd, zodat u geen berichten kunt versturen. Neem contact op met een beheerder om de versleuteling in een geldige staat te herstellen.
- Toon bericht bubbels
+ Berichtbubbels weergevenKan kaart niet ladenKaartLet op: app wordt opnieuw gestart
@@ -2393,7 +2393,7 @@
%1$d meer%1$d meer
- Toon minder
+ Minder weergevenLocatie delen is bezig${app_name} Live locatieStop
@@ -2471,8 +2471,8 @@
\n
\nHoud er rekening mee dat deze actie de app opnieuw zal starten en dat dit enige tijd kan duren.
Initieel synchronisatieverzoek
- Toon de laatste profielinformatie (avatar en weergavenaam) voor alle berichten.
- Toon laatste persoonsinformatie
+ Geeft de meest recente gebruikersinfo (avatar en weergavenaam) weer voor alle berichten.
+ Meest recente gebruikersinfo weergevenBezetBack-up heeft een geldige handtekening van deze persoon.%1$s geleden bijgewerkt
@@ -2563,7 +2563,7 @@
%s moet uw account verifiërenVul uw e-mailadres inLees de voorwaarden en het beleid van %s door
- Serverbeleid
+ ServerbeleidenNeem contact opElement Matrix Services (EMS) is een robuuste en betrouwbare hostingservice voor snelle, veilige en realtime communicatie. Ontdek hoe op element.io/emsWilt u uw eigen server hosten\?
@@ -2596,15 +2596,15 @@
%1$s en %2$sE-mailadres niet geverifieerd, controleer je inbox
- Toon alle sessies (V2, WIP)
+ Alle sessies weergeven (V2, WIP)Kan kaart niet laden
\nDeze server is mogelijk niet geconfigureerd om kaarten weer te geven.Open instellingen
- Voor de beste beveiliging verifieert u uw sessies en meldt u zich af bij elke sessie die u niet meer herkent of gebruikt.
- Andere sessies
+ Voor de beste beveiliging verifieert u uw sessies en meldt u zich af bij elke sessie die u niet meer herkent of gebruikt.
+ Andere sessiesSessiesLijst met publieke spaces
- Maak een nieuw gesprek of een nieuwe kamer
+ Nieuw gesprek of nieuwe kamer aanmakenPersonenFavorietenOngelezen
@@ -2613,10 +2613,59 @@
ActiviteitSorteer opRecente tonen
- Toon filters
+ Filters weergevenLay-outvoorkeurenOntdek kamersKamer creëren
- Start gesprek
+ Gesprek startenAlle gesprekken
+ U kunt feedback geven via het menu rechtsboven.
+ Krijg sneller en gemakkelijker toegang tot uw ruimten (rechtsonder).
+ Om ${app_name} te versimpelen zijn tabbladen nu optioneel. U kunt ze beheren in het menu rechtsboven.
+ Hier zullen uw ongelezen berichten verschijnen wanneer u deze heeft.
+ De allesomvattende beveiligde chat-app voor teams, vrienden en organisaties. Maak een gesprek aan of word deelnemer van een bestaande kamer om te beginnen.
+ Ruimten zijn een nieuwe manier om kamers en personen te groeperen. Voeg een bestaande kamer toe, of maak een nieuwe aan via de knop rechtsonder.
+
+ Overweeg uit te loggen van oude sessies (%1$d of meer dagen) welke u niet meer gebruikt.
+ Overweeg uit te loggen van oude sessies (%1$d of meer dagen) welke u niet meer gebruikt.
+
+ Verifieer of log uit van ongeverifieerde sessies.
+ Verbeter uw accountbeveiliging door deze aanbevelingen te volgen.
+
+ Al %1$d+ dag inactief (%2$s)
+ Al %1$d+ dagen inactief (%2$s)
+
+ Beveiligingsaanbevelingen
+ Niet-geverifieerde sessies
+ Inactieve sessies
+ %s
+\nziet er vrij leeg uit.
+ Welkom bij ${app_name},
+\n%s.
+ Niets te melden.
+ Welkom bij een nieuw uiterlijk!
+ Toegang tot ruimten
+ Feedback geven
+ Uitproberen
+ Niet-geverifieerd · Laatste activiteit %1$s
+ Geverifieerd · Laatste activiteit %1$s
+ Alle bekijken (%1$d)
+ Details bekijken
+ Sessie verifiëren
+ Sorry, deze kamer kon niet worden gevonden.
+\nProbeer het later opnieuw. %s
+ Dit is waar uw nieuwe verzoeken en uitnodigingen zullen verschijnen.
+ Ruimten zijn een nieuwe manier om kamers en personen te groeperen. Maak een ruimte aan om te beginnen.
+ Ongeverifieerde sessie
+ Geverifieerde sessie
+ Onbekend apparaattype
+ Desktop
+ Web
+ Mobiel
+ Niets nieuws.
+ Uitnodigingen
+ Nog geen ruimten.
+ %s subitems inklappen
+ %s subitems uitvouwen
+ Ruimte aanpassen
diff --git a/library/ui-strings/src/main/res/values-nn/strings.xml b/library/ui-strings/src/main/res/values-nn/strings.xml
index a56ba0ac30..45c8679736 100644
--- a/library/ui-strings/src/main/res/values-nn/strings.xml
+++ b/library/ui-strings/src/main/res/values-nn/strings.xml
@@ -310,7 +310,7 @@
PregNoko gjekk gale med dekrypteringaOffentleg namn
- Økt-ID
+ Økt-IDSesjonsnøkkelEksporter E2E-romnøkklarEksporter romnøkklar
diff --git a/library/ui-strings/src/main/res/values-pl/strings.xml b/library/ui-strings/src/main/res/values-pl/strings.xml
index 18b0de078c..b7b73eb9e6 100644
--- a/library/ui-strings/src/main/res/values-pl/strings.xml
+++ b/library/ui-strings/src/main/res/values-pl/strings.xml
@@ -145,7 +145,7 @@
Przejdź do pierwszej nieprzeczytanej wiadomościOpuść pokójCzy na pewno chcesz opuścić pokój?
- WIADOMOŚCI BEZPOŚREDNIE
+ Wiadomości bezpośrednieZaprośBlokujOdbanuj
@@ -231,7 +231,7 @@
Ustaw jako główny adresMotywNazwa publiczna
- ID sesji
+ ID sesjiEksportujWprowadź hasłoPotwierdź hasło
@@ -548,7 +548,7 @@
PokojeDodaj reakcjęUtwórz nowy pokój
- Wiadomości Bezpośrednie
+ Wiadomości bezpośrednieSTWÓRZNazwaPubliczny
@@ -654,7 +654,7 @@
Proszę wykonać kopięPreferencjeGłos i wideo
- Wiadomości Bezpośrednie
+ Wiadomości bezpośrednieFiltruj rozmowy…Wyślij nową wiadomość bezpośredniąWersja Matrix SDK
@@ -841,7 +841,7 @@
Url:Format:Zarejestruj token
- Dziękujemy, sugestia została szczęśliwie wysłana
+ Dziękujemy, sugestia została pomyślnie wysłanaWysłanie sugestii nie powiodło się (%s)Wyświetl ukryte wydarzenia na linii czasowej(edytowano)
@@ -2697,8 +2697,8 @@
Nie można wczytać mapy.
\nTen serwer macierzysty może nie być skonfigurowany do wyświetlania map.Otwórz ustawienia
- Aby zapewnić najlepsze bezpieczeństwo, zweryfikuj swoje sesje i wyloguj się z każdej sesji, której już nie rozpoznajesz lub której już nie używasz.
- Inne sesje
+ Aby zapewnić najlepsze bezpieczeństwo, zweryfikuj swoje sesje i wyloguj się z każdej sesji, której już nie rozpoznajesz lub której już nie używasz.
+ Inne sesjeSesjeLista otwartych przestrzeniUtwórz nową rozmowę lub pokój
@@ -2721,10 +2721,8 @@
Nie zweryfikowano · Ostatnia aktywność %1$sZweryfikowano · Ostatnia aktywność %1$sPokaż wszystkie (%1$d)
- Obecna sesjaPokaż szczegółyZweryfikuj sesję
- Twoja obecna sesja jest przygotowana do bezpiecznej komunikacji.Niezweryfikowana sesjaZweryfikowana sesjaNieznany typ urządzenia
@@ -2734,4 +2732,4 @@
Niestety, ten pokój nie został znaleziony.
\nSpróbuj ponownie później.%sZaproszenia
-
\ No newline at end of file
+
diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
index 08c41db365..817c7646df 100644
--- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
@@ -1,6 +1,6 @@
- convite de %s
+ Convite de %s%1$s convidou %2$s%1$s convidou você%1$s juntou-se à sala
@@ -418,7 +418,7 @@
Des-definir como endereço principalErro de decriptaçãoNome público
- ID de sessão
+ ID de sessãoChave de sessãoExportar chaves de sala E2EExportar chaves de sala
@@ -2320,7 +2320,7 @@
Enviar imagens e vídeosAbrir câmeraSeu sistema vai automaticamente enviar logs quando um erro incapaz de decriptar ocorre
- Auro Reportar Erros de Decriptação.
+ Auto Reportar Erros de Decriptação.Sobrepor cor de nome de exibiçãoEu já tenho uma contaMensageria segura.
@@ -2477,7 +2477,7 @@
Backup tem uma assinatura válida desta(e) usuária(o).Atualizada %1$s atrásImplementação tempoária: locais persistem em histórico de sala
- Habilitar Compartilhament de Localização Ao Vivo
+ Habilitar Compartilhamento de Localização Ao Vivo%1$s restandoAo vivo até %1$sVer localização ao vivo
@@ -2499,7 +2499,7 @@
EndpointGatewayAtivar compartilhamento de localização
- Compartilhamento de localização em tempo real
+ Compartilhamento de localização ao vivoGateway atual: %sNão foi possível encontrar o endpoint.Endpoint atual: %s
@@ -2538,7 +2538,7 @@
Nome de Usuária(o) / Email / TelefoneVocê é um/uma humano(a)\?Siga as instruções enviadas para %s
- Reset de senha
+ Senha resettadaEsqueceu senhaReenviar emailNão recebeu um email\?
@@ -2601,8 +2601,8 @@
Abrir configuraçõesTodos os ChatsMostrar Todas Sessões (V2, WIP)
- Para a melhor segurança, verifique suas sessões e faça signout de qualquer sessão que você não reconhece ou usa mais.
- Outras sessões
+ Para a melhor segurança, verifique suas sessões e faça signout de qualquer sessão que você não reconhece ou usa mais.
+ Outras sessõesSessõesAbrir lista de espaçosCriar uma nova conversa ou sala
@@ -2622,11 +2622,8 @@
Não-verificada · Última atividade %1$sVerificada · Última atividade %1$sVer Todas (%1$d)
- Sessão AtualVisualizar DetalhesVerificar Sessão
- Verifique sua sessão atual para mensageria segura melhorada.
- Sua sessão atual está pronta para mensageria segura.Sessão não-verificadaSessão verificadaTipo de dispositivo desconhecido
@@ -2636,4 +2633,39 @@
Desculpe, esta sala não tem sido encontrada.
\nPor favor retente mais tarde.%sConvites
+ Teste aí
+ Toque na direita topo para ver a opção para feedback.
+ Dar Feedback
+ Acessar seus Espaços (direito fundo) mais rápido e fácio que jamais antes.
+ Acessar Espaços
+ Para simplificar seu ${app_name}, abas são agora opcionais. Gerencie-as usando o menu direito topo.
+ Boas-vindas a uma nova visão!
+ Isto é onde suas mensagens não-lidas vão aparecer, quando você tiver algumas.
+ Nada a reportar.
+ O app de chat seguro tudo-em-um para equipes, amigas(os) e organizações. Crie um chat, ou junte-se a uma sala existe, para começar.
+ Boas-vindas a ${app_name},
+\n%s.
+ Espaços são uma nova maneira de agrupar salas e pessoas. Adicione uma sala existente, ou crie uma nova, usando o botão direito fundo.
+ %s
+\nestá parecendo um pouco vazio.
+
+ Considere fazer signout de sessões antigas (%1$d dia ou mais) que você não usa mais.
+ Considere fazer signout de sessões antigas (%1$d dias ou mais) que você não usa mais.
+
+ Sessões inativas
+ Verificar ou fazer signout de sessões não-verificadas.
+ Sessões não-verificadas
+ Melhore a segurança de sua conta ao seguir estas recomendações.
+ Recomendações de segurança
+
+ Inativa(o) por %1$d+ dia (%2$s)
+ Inativa(o) por %1$d+ dias (%2$s)
+
+ Isto é onde suas novas requisições e convites vão estar.
+ Nada novo.
+ Espaços são uma nova maneira de agrupar salas e pessoas. Crie um espaço para começar.
+ Nenhum espaço ainda.
+ Colapsar filhos de %s
+ Expandir filhos de %s
+ Mudar Espaço
diff --git a/library/ui-strings/src/main/res/values-pt/strings.xml b/library/ui-strings/src/main/res/values-pt/strings.xml
index 4daaef83b0..87b6297b2b 100644
--- a/library/ui-strings/src/main/res/values-pt/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt/strings.xml
@@ -246,7 +246,7 @@ Note que esta acção irá reiniciar a aplicação e poderá levar algum tempo.<
Erro de decifragemNome do dispositivo
- ID do dispositivo
+ ID do dispositivoChave do dispositivoExportar chaves E2E da salaExportar chaves de sala
diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index 4852be1f82..7c9d073035 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -432,7 +432,7 @@
Сбросить основной адресОшибка дешифровкиПубличное имя
- ID сессии
+ ID сессииКлюч сессииЭкспорт E2E ключей комнатыЭкспорт ключей комнаты
@@ -2660,8 +2660,8 @@
Не удалось загрузить карту
\nВозможно, этот домашний сервер не настроен для отображения карт.Все беседы
- Для лучшей безопасности заверьте свои сессии и выйдите из тех, которые более не признаёте или не используете.
- Другие сессии
+ Для лучшей безопасности заверьте свои сессии и выйдите из тех, которые более не признаёте или не используете.
+ Другие сессииСессииСоздать беседу или комнатуПоказать все сессии (V2, в разработке)
@@ -2678,4 +2678,19 @@
Обзор комнатНачать беседуСоздать комнату
-
\ No newline at end of file
+ Посмотреть все (%1$d)
+ Повысьте безопасность учётной записи, следуя этим рекомендациям.
+ Заверенная · Последняя активность %1$s
+ Незаверенная сессия
+ Заверенная сессия
+ Неизвестный тип устройства
+ Компьютер
+ Мобильный
+ Незаверенная · Последняя активность %1$s
+ Рекомендации по безопасности
+ Незаверенные сессии
+ Неактивные сессии
+ Добро пожаловать в ${app_name},
+\n%s.
+ Оставить отзыв
+
diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index 2cc2d0280e..9eb22e3ae3 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -388,7 +388,7 @@
VzhľadChyba dešifrovaniaVerejné meno
- ID relácie
+ ID relácieKľúč relácieExportovať šifrovacie kľúče miestnostiExportovať kľúče miestnosti
@@ -2651,8 +2651,8 @@
Otvoriť nastaveniaVšetky konverzácieZobraziť všetky relácie (V2, WIP)
- V záujme čo najlepšieho zabezpečenia overte svoje relácie a odhláste sa z každej relácie, ktorú už nepoznáte alebo nepoužívate.
- Iné relácie
+ V záujme čo najlepšieho zabezpečenia overte svoje relácie a odhláste sa z každej relácie, ktorú už nepoznáte alebo nepoužívate.
+ Iné relácieRelácieOtvoriť zoznam priestorovVytvoriť novú konverzáciu alebo miestnosť
@@ -2672,11 +2672,8 @@
Neoverené - Posledná aktivita %1$sOverené - Posledná aktivita %1$sZobraziť všetky (%1$d)
- Aktuálna reláciaZobraziť podrobnostiOveriť reláciu
- Overte svoju aktuálnu reláciu pre vylepšené bezpečné zasielanie správ.
- Vaša aktuálna relácia je pripravená na bezpečné zasielanie správ.Neoverená reláciaOverená reláciaNeznámy typ zariadenia
@@ -2686,4 +2683,41 @@
Je nám ľúto, táto miestnosť nebola nájdená.
\nProsím, skúste to neskôr.%sPozvánky
+ Vyskúšajte si to
+ Ťuknutím na položku vpravo hore zobrazíte možnosť spätnej väzby.
+ Poskytnite spätnú väzbu
+ Získajte prístup k svojim priestorom (vľavo dole) rýchlejšie a jednoduchšie ako kedykoľvek predtým.
+ Prístup k priestorom
+ Pre zjednodušenie vašej aplikácie ${app_name}, sú teraz karty voliteľné. Spravujte ich pomocou ponuky vpravo hore.
+ Vitajte v novom zobrazení!
+ Tu sa zobrazia neprečítané správy, ak nejaké máte.
+ Nič, o čom by bolo potrebné podať správu.
+ Kompletná zabezpečená aplikácia na komunikáciu pre tímy, priateľov a organizácie. Začnite konverzáciu alebo sa pridajte k existujúcej miestnosti.
+ Vitajte v aplikácii ${názov_aplikácie},
+\n%s.
+ Priestory sú novým spôsobom zoskupovania miestností a ľudí. Pomocou tlačidla vpravo dole môžete pridať existujúcu miestnosť alebo vytvoriť novú.
+ %s
+\nvyzerá trochu prázdne.
+
+ Zvážte odhlásenie zo starých relácií (%1$d deň alebo viac), ktoré už nepoužívate.
+ Zvážte odhlásenie zo starých relácií (%1$d dni alebo viac), ktoré už nepoužívate.
+ Zvážte odhlásenie zo starých relácií (%1$d dní alebo viac), ktoré už nepoužívate.
+
+ Neaktívne relácie
+ Overte alebo sa odhláste z neoverených relácií.
+ Neoverené relácie
+ Zlepšite zabezpečenie svojho účtu dodržiavaním týchto odporúčaní.
+ Bezpečnostné odporúčania
+
+ Neaktívny už %1$d+ deň (%2$s)
+ Neaktívny už %1$d+ dni (%2$s)
+ Neaktívny už %1$d+ dní (%2$s)
+
+ Tu sa budú nachádzať vaše nové žiadosti a pozvánky.
+ Nič nové.
+ Priestory sú novým spôsobom zoskupovania miestností a ľudí. Vytvorte si priestor a začnite.
+ Zatiaľ žiadne priestory.
+ Zbaliť %s podpriestory
+ Rozbaliť %s podpriestory
+ Zmeniť priestor
diff --git a/library/ui-strings/src/main/res/values-sq/strings.xml b/library/ui-strings/src/main/res/values-sq/strings.xml
index 8fdf4ee310..a6af0a4921 100644
--- a/library/ui-strings/src/main/res/values-sq/strings.xml
+++ b/library/ui-strings/src/main/res/values-sq/strings.xml
@@ -431,7 +431,7 @@
TemëGabim shfshehtëzimiEmër publik
- ID Sesioni
+ ID SesioniKyç sesioniEksporto kyçe dhome E2EEksporto kyçe dhome
diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml
index 025713272c..30b63c213c 100644
--- a/library/ui-strings/src/main/res/values-sv/strings.xml
+++ b/library/ui-strings/src/main/res/values-sv/strings.xml
@@ -918,7 +918,7 @@
Sätt upp på den här enhetenGenerera en ny säkerhetskopia eller sätt en ny lösenfras för din existerande säkerhetskopia.Detta är experimentella funktioner som kan gå sönder på oväntade sätt. Använd varsamt.
- Sessions-ID
+ Sessions-IDSessionsnyckelExportera krypteringsnycklarExportera rumsnycklar
diff --git a/library/ui-strings/src/main/res/values-te/strings.xml b/library/ui-strings/src/main/res/values-te/strings.xml
index 5ed2462ce8..0154d54c2e 100644
--- a/library/ui-strings/src/main/res/values-te/strings.xml
+++ b/library/ui-strings/src/main/res/values-te/strings.xml
@@ -260,7 +260,7 @@
ప్రధాన చిరునామాగా సెట్ చేయండిపరికరం పేరు
- పరికరం ID
+ పరికరం IDపరికరం కీE2E గది కీలను ఎగుమతి చేయండి
diff --git a/library/ui-strings/src/main/res/values-tr/strings.xml b/library/ui-strings/src/main/res/values-tr/strings.xml
index c097bfce6a..1f0e5be153 100644
--- a/library/ui-strings/src/main/res/values-tr/strings.xml
+++ b/library/ui-strings/src/main/res/values-tr/strings.xml
@@ -376,7 +376,7 @@
TemaÇözme hatasıGörünür Ad
- Oturum kimliği
+ Oturum kimliğiOturum anahtarıE2E Oda anahtarlarını dışa aktarOda anahtarlarını dışa aktar
diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml
index 1c809fff3e..3e511f8459 100644
--- a/library/ui-strings/src/main/res/values-uk/strings.xml
+++ b/library/ui-strings/src/main/res/values-uk/strings.xml
@@ -354,7 +354,7 @@
Зробити не основною адресоюПомилка розшифруванняЗагальнодоступна назва
- ID сеансу
+ ID сеансуКлюч сеансуЕкспортувати E2E ключі кімнатиЕкспортувати ключі кімнати
@@ -2701,8 +2701,8 @@
Відкрити налаштуванняУсі бесідиПоказати всі сеанси (V2, WIP)
- Для найкращої безпеки перевірте свої сеанси та вийдіть з усіх сеансів, які ви більше не розпізнаєте або не використовуєте.
- Інші сеанси
+ Для найкращої безпеки перевірте свої сеанси та вийдіть з усіх сеансів, які ви більше не розпізнаєте або не використовуєте.
+ Інші сеансиСеансиВідкрити список кімнатСтворити нову розмову або кімнату
@@ -2722,11 +2722,8 @@
Не звірений · Остання активність %1$sЗвірений · Остання активність %1$sПереглянути всі (%1$d)
- Поточний сеансПереглянути подробиціЗвірити сеанс
- Звірте свій поточний сеанс для безпечнішого обміну повідомленнями.
- Ваш поточний сеанс готовий для безпечного обміну повідомленнями.Не звірений сеансЗвірений сеансНевідомий тип пристрою
@@ -2736,4 +2733,43 @@
Перепрошуємо, цю кімнату не знайдено.
\nСпробуйте пізніше.%sЗапрошення
+ Спробувати
+ Клацніть праворуч вгорі, щоб побачити опцію відгуку.
+ Надіслати відгук
+ Отримуйте доступ до своїх просторів (унизу праворуч) швидше та легше, ніж раніше.
+ Доступ до просторів
+ Щоб спростити ваш ${app_name}, вкладки тепер необов’язкові. Керуйте ними у верхньому правому меню.
+ Вітаємо в новому вигляді!
+ Тут з\'являтимуться ваші непрочитані повідомлення, якщо вони є.
+ Немає про що звітувати.
+ Універсальний безпечний застосунок для спілкування з командами, друзями й організаціями. Створіть бесіду або приєднайтеся до наявної кімнати, щоб розпочати.
+ Вітаємо в ${app_name},
+\n%s.
+ Простори – це новий спосіб групувати кімнати та людей. Додайте наявну кімнату або створіть нову, використовуючи кнопку внизу праворуч.
+ %s
+\nмає дещо порожній вигляд.
+
+ Зважте потребу вийти зі старих сеансів (%1$d день або більше), який ви більше не використовуєте.
+ Зважте потребу вийти зі старих сеансів (%1$d дні або більше), які ви більше не використовуєте.
+ Зважте потребу вийти зі старих сеансів (%1$d днів або більше), які ви більше не використовуєте.
+ Зважте потребу вийти зі старих сеансів (%1$d днів або більше), які ви більше не використовуєте.
+
+ Неактивні сеанси
+ Звірити або вийти з не звірених сеансів.
+ Не звірений сеанс
+ Удоскональте безпеку свого облікового запису, дотримуючись цих порад.
+ Поради щодо безпеки
+
+ Без активності %1$d+ день (%2$s)
+ Без активності %1$d+ дні (%2$s)
+ Без активності %1$d+ днів (%2$s)
+ Без активності %1$d+ днів (%2$s)
+
+ Тут з\'являтимуться нові запити та запрошення.
+ Нічого нового.
+ Простори – це новий спосіб групувати кімнати та людей. Створіть простір, щоб розпочати.
+ Ще немає просторів.
+ Згорнути дочірні елементи %s
+ Розгорнути дочірні елементи %s
+ Змінити простір
diff --git a/library/ui-strings/src/main/res/values-vi/strings.xml b/library/ui-strings/src/main/res/values-vi/strings.xml
index 2803128843..c6dc97f782 100644
--- a/library/ui-strings/src/main/res/values-vi/strings.xml
+++ b/library/ui-strings/src/main/res/values-vi/strings.xml
@@ -594,7 +594,7 @@
Hủy tài khoảnXem lại ngayChìa khóa phiên
- Mã phiên
+ Mã phiênTên công khaiLỗi giải mãNhững chức năng này mang tính thí nghiệm có thể còn nhiều lỗi. Lưu ý khi dùng.
diff --git a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
index 4e1c8e61c8..db1dab92e2 100644
--- a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
@@ -242,7 +242,7 @@
你的密码已更新解密错误公开名称
- 会话 ID
+ 会话 ID会话密钥导入已验证
@@ -2500,7 +2500,7 @@
若启用,即使正在使用应用,你也会对其他用户显示为离线状态。离线模式在场
- 动画图片一出现就在时间轴中播放
+ 动画图片一出现就在时间线中播放Threads Beta${app_name} needs to perform a clear cache to be up to date, 原因如下:
\n%s
@@ -2551,8 +2551,8 @@
打开设置全部聊天显示全部会话(V2, WIP)
- 为获得最佳安全性,请验证你的会话,并从任何你不认识或不再使用的会话登出。
- 其他会话
+ 为获得最佳安全性,请验证你的会话,并从任何你不认识或不再使用的会话登出。
+ 其他会话会话打开空间列表创建新对话或房间
@@ -2574,13 +2574,34 @@
未验证 · 上次活跃 %1$s已验证 · 上次活跃 %1$s查看全部(%1$d)
- 当前会话查看详情验证会话
- 为了获得增强的安全的消息传送,请验证你当前的会话。
- 你的当前会话已准备好安全地收发消息。未验证的会话已验证的会话未知的设备类型邀请
-
\ No newline at end of file
+ 移动设备
+ Web
+ 桌面
+ 更改空间
+ 尚无空间。
+ 没有新的东西。
+ 你的新请求和邀请会在这里。
+
+ %1$d+天不活跃(%2$s)
+
+ 安全建议
+ 按照这些建议改善你的账户安全。
+ 未验证的会话
+ 验证未验证的会话或从之登出。
+ 不活跃的会话
+
+ 请考虑从不再使用的旧会话(%1$d天或更久)登出。
+
+ 欢迎来到${app_name},
+\n%s。
+ 未读消息会在这里显示。
+ 提供反馈
+ 点击右上角查看反馈选项。
+ 试用
+
diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
index 0f5208bcde..78caa2cc2e 100644
--- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
@@ -469,7 +469,7 @@
主題解密錯誤公開名稱
- 工作階段 ID
+ 工作階段 ID工作階段金鑰匯出聊天室的端到端加密金鑰匯出聊天室的加密金鑰
@@ -2551,8 +2551,8 @@
開啟設定所有聊天顯示所有工作階段 (V2, WIP)
- 為了取得最佳安全性,請驗證您的工作階段並登出任何您無法識別或不再使用的工作階段。
- 其他工作階段
+ 為了取得最佳安全性,請驗證您的工作階段並登出任何您無法識別或不再使用的工作階段。
+ 其他工作階段工作階段開啟空間清單建立新的對話或聊天室
@@ -2572,11 +2572,8 @@
未驗證 · 最後活動 %1$s已驗證 · 最後活動 %1$s檢視全部 (%1$d)
- 目前工作階段檢視詳細資訊驗證工作階段
- 驗證您目前的工作階段以強化安全通訊。
- 您目前的工作階段已準備好進行安全通訊。未驗證的工作階段已驗證的工作階段未知的裝置類型
@@ -2586,4 +2583,37 @@
抱歉,找不到此聊天室。
\n請稍後再試。%s邀請
+ 試試看
+ 輕點右上角來檢視回饋選項。
+ 給予回饋
+ 存取您的空間(右下角)比以往任何時候都更快且更輕鬆。
+ 存取空間
+ 為了簡化您的 ${app_name},分頁現在是選擇性的。使用右上角的選單管理它們。
+ 歡迎使用新的檢視!
+ 當您有一些未讀的訊息時,這裡會顯示您的未讀訊息。
+ 沒有要回報的東西。
+ 適用於團隊、朋友與組織的多合一安全聊天應用程式。建立聊天室,或加入一個既有的聊天室。
+ 歡迎使用 ${app_name},
+\n%s.
+ 空間是一種為聊天室與人們分組的新方式。使用右下角的按鈕新增既有的聊天室或建立新的。
+ %s
+\n看起來有點空。
+
+ 考慮登出您不再使用的舊工作階段(%1$d天或更久)。
+
+ 不活躍的工作階段
+ 驗證或從未驗證的工作階段登出。
+ 未驗證的工作階段
+ 按照這些建議提高您的帳號安全性。
+ 安全建議
+
+ 不活躍 %1$d+ 天 (%2$s)
+
+ 這是您的新請求與邀請的所在。
+ 沒有新東西。
+ 空間是一種對聊天室與人們分組的新方式。建立空間以開始。
+ 尚無空間。
+ 折疊 %s 個子空間
+ 展開 %s 個子空間
+ 變更空間
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 6a87ce82f4..dec46159dd 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -442,6 +442,9 @@
Enable new layoutA simplified Element with optional tabs
+ Enable deferred DMs
+ Create DM only on first message
+
InvitesLow priority
@@ -1212,7 +1215,6 @@
Decryption errorPublic name
- Session IDSession keyExport E2E room keys
@@ -2362,8 +2364,8 @@
Manage SessionsSign out of this sessionSessions
- Other sessions
- For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore.
+ Other sessions
+ For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore.Server nameServer version
@@ -3230,18 +3232,12 @@
Unknown device typeVerified sessionUnverified session
-
- Your current session is ready for secure messaging.
-
- Verify your current session for enhanced secure messaging.Your current session is ready for secure messaging.This session is ready for secure messaging.Verify your current session for enhanced secure messaging.Verify or sign out from this session for best security and reliability.Verify SessionView Details
-
- Current SessionView All (%1$d)Verified · Last activity %1$s
@@ -3263,8 +3259,40 @@
Current SessionSession
+ DeviceLast activity %1$s
+ Filter
+ All sessions
+ Verified
+ Ready for secure messaging
+ Unverified
+ Not ready for secure messaging
+ Inactive
+
+ Inactive for %1$d day or longer
+ Inactive for %1$d days or longer
+
+ Filter
+ Verified
+ For best security, sign out from any session that you don’t recognize or use anymore.
+ Unverified
+ Verify your sessions for enhanced secure messaging or sign out from those you don’t recognize or use anymore.
+ Inactive
+
+ Consider signing out from old sessions (%1$d day or more) you don’t use anymore.
+ Consider signing out from old sessions (%1$d days or more) you don’t use anymore.
+
+ No verified sessions found.
+ No unverified sessions found.
+ No inactive sessions found.
+ Clear Filter
+ Session details
+ Application, device, and activity information.
+ Session name
+ Session ID
+ Last activity
+ IP address%s\nis looking a little empty.
diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml
index 01af740d43..3d6bc91f2e 100644
--- a/library/ui-styles/src/main/res/values/colors.xml
+++ b/library/ui-styles/src/main/res/values/colors.xml
@@ -141,6 +141,7 @@
#0DBD8B
+ #0F0DBD8B#17191C#FF4B55#0FFF4B55
diff --git a/library/ui-styles/src/main/res/values/stylable_other_sessions_security_recommendation_view.xml b/library/ui-styles/src/main/res/values/stylable_other_sessions_security_recommendation_view.xml
new file mode 100644
index 0000000000..6a46132b13
--- /dev/null
+++ b/library/ui-styles/src/main/res/values/stylable_other_sessions_security_recommendation_view.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/ui-styles/src/main/res/values/stylable_session_overview_entry_view.xml b/library/ui-styles/src/main/res/values/stylable_session_overview_entry_view.xml
new file mode 100644
index 0000000000..d3884f247d
--- /dev/null
+++ b/library/ui-styles/src/main/res/values/stylable_session_overview_entry_view.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/library/ui-styles/src/main/res/values/stylable_devices_list_header_view.xml b/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
similarity index 51%
rename from library/ui-styles/src/main/res/values/stylable_devices_list_header_view.xml
rename to library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
index 97e0290815..d3b931e44a 100644
--- a/library/ui-styles/src/main/res/values/stylable_devices_list_header_view.xml
+++ b/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
@@ -2,8 +2,8 @@
-
-
+
+
diff --git a/library/ui-styles/src/main/res/values/styles_devices_management.xml b/library/ui-styles/src/main/res/values/styles_devices_management.xml
index 6fb236d3e6..6b42b85ffd 100644
--- a/library/ui-styles/src/main/res/values/styles_devices_management.xml
+++ b/library/ui-styles/src/main/res/values/styles_devices_management.xml
@@ -1,6 +1,10 @@
+
+
diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt
index 8be8e83569..a6b4cc98a6 100644
--- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt
+++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -46,6 +47,13 @@ class FlowRoom(private val room: Room) {
}
}
+ fun liveLocalRoomSummary(): Flow> {
+ return room.getLocalRoomSummaryLive().asFlow()
+ .startWith(room.coroutineDispatchers.io) {
+ room.localRoomSummary().toOptional()
+ }
+ }
+
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow> {
return room.membershipService().getRoomMembersLive(queryParams).asFlow()
.startWith(room.coroutineDispatchers.io) {
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 7b06edb530..65f8baee7b 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -60,7 +60,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
- buildConfigField "String", "SDK_VERSION", "\"1.4.36\""
+ buildConfigField "String", "SDK_VERSION", "\"1.5.0\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt
index d3f6ec2287..1d6e79c8f6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt
@@ -68,6 +68,11 @@ sealed interface QueryStringValue {
*/
data class Contains(override val string: String, override val case: Case = Case.SENSITIVE) : ContentQueryStringValue
+ /**
+ * The tested field must not contain the [string].
+ */
+ data class NotContains(override val string: String, override val case: Case = Case.SENSITIVE) : ContentQueryStringValue
+
/**
* Case enum for [ContentQueryStringValue].
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/SessionExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/SessionExtensions.kt
index a15e73eb88..96dac27618 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/SessionExtensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/SessionExtensions.kt
@@ -32,5 +32,13 @@ fun Session.getRoomSummary(roomIdOrAlias: String): RoomSummary? = roomService().
/**
* Get a user using the UserService of a Session.
+ * @param userId the userId to look for.
+ * @return a user with userId or null if the User is not known yet by the SDK.
+ * See [org.matrix.android.sdk.api.session.user.UserService.resolveUser] to ensure that a User is retrieved.
*/
fun Session.getUser(userId: String): User? = userService().getUser(userId)
+
+/**
+ * Similar to [getUser], but fallback to a User without details if the User is not known by the SDK, or if Session is null.
+ */
+fun Session?.getUserOrDefault(userId: String): User = this?.userService()?.getUser(userId) ?: User(userId)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt
index 5d2769ac3c..8031fcaeea 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt
@@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
import org.matrix.android.sdk.api.session.room.location.LocationSharingService
import org.matrix.android.sdk.api.session.room.members.MembershipService
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.relation.RelationService
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
@@ -60,11 +61,22 @@ interface Room {
*/
fun getRoomSummaryLive(): LiveData>
+ /**
+ * A live [LocalRoomSummary] associated with the room.
+ * You can observe this summary to get dynamic data from this room.
+ */
+ fun getLocalRoomSummaryLive(): LiveData>
+
/**
* A current snapshot of [RoomSummary] associated with the room.
*/
fun roomSummary(): RoomSummary?
+ /**
+ * A current snapshot of [LocalRoomSummary] associated with the room.
+ */
+ fun localRoomSummary(): LocalRoomSummary?
+
/**
* Use this room as a Space, if the type is correct.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
index ad8106c9c1..65383f1007 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
@@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.identity.model.SignInvitationResult
import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -117,6 +118,12 @@ interface RoomService {
*/
fun getRoomSummaryLive(roomId: String): LiveData>
+ /**
+ * A live [LocalRoomSummary] associated with the room with id [roomId].
+ * You can observe this summary to get dynamic data from this room, even if the room is not joined yet
+ */
+ fun getLocalRoomSummaryLive(roomId: String): LiveData>
+
/**
* Get a snapshot list of room summaries.
* @return the immutable list of [RoomSummary]
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt
index 60963ef25a..d651f06e23 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt
@@ -20,8 +20,10 @@ import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.query.SpaceFilter
+import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams.Builder
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomType
+import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
/**
@@ -52,6 +54,10 @@ fun spaceSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) =
* [roomSummaryQueryParams] and [spaceSummaryQueryParams] can also be used to build an instance of this class.
*/
data class RoomSummaryQueryParams(
+ /**
+ * Query for the roomId.
+ */
+ val roomId: QueryStringValue,
/**
* Query for the displayName of the room. The display name can be the value of the state event,
* or a value returned by [org.matrix.android.sdk.api.RoomDisplayNameFallbackProvider].
@@ -94,6 +100,7 @@ data class RoomSummaryQueryParams(
* [roomSummaryQueryParams] and [spaceSummaryQueryParams] can also be used to build an instance of [RoomSummaryQueryParams].
*/
class Builder {
+ var roomId: QueryStringValue = QueryStringValue.NotContains(RoomLocalEcho.PREFIX)
var displayName: QueryStringValue = QueryStringValue.NoCondition
var canonicalAlias: QueryStringValue = QueryStringValue.NoCondition
var memberships: List = Membership.all()
@@ -104,6 +111,7 @@ data class RoomSummaryQueryParams(
var spaceFilter: SpaceFilter = SpaceFilter.NoFilter
fun build() = RoomSummaryQueryParams(
+ roomId = roomId,
displayName = displayName,
canonicalAlias = canonicalAlias,
memberships = memberships,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomCreationState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomCreationState.kt
new file mode 100644
index 0000000000..4fc99225c4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomCreationState.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.api.session.room.model
+
+enum class LocalRoomCreationState {
+ NOT_CREATED,
+ CREATING,
+ FAILURE,
+ CREATED
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomSummary.kt
new file mode 100644
index 0000000000..eced1dd581
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/LocalRoomSummary.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.api.session.room.model
+
+import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+
+/**
+ * This class holds some data of a local room.
+ * It can be retrieved by [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
+ */
+data class LocalRoomSummary(
+ /**
+ * The roomId of the room.
+ */
+ val roomId: String,
+ /**
+ * The room summary of the room.
+ */
+ val roomSummary: RoomSummary?,
+ /**
+ * The creation params attached to the room.
+ */
+ val createRoomParams: CreateRoomParams?,
+ /**
+ * The roomId of the created room (ie. created on the server), if any.
+ */
+ val replacementRoomId: String?,
+ /**
+ * The creation state of the room.
+ */
+ val creationState: LocalRoomCreationState,
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt
index 7d3109fb6e..2388bee0ee 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt
@@ -34,5 +34,4 @@ data class SpaceChildInfo(
val canonicalAlias: String?,
val aliases: List?,
val worldReadable: Boolean
-
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/RoomLocalEcho.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/RoomLocalEcho.kt
index 7ef0d63924..ec0e642ad3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/RoomLocalEcho.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/RoomLocalEcho.kt
@@ -20,7 +20,7 @@ import java.util.UUID
object RoomLocalEcho {
- private const val PREFIX = "!local."
+ const val PREFIX = "!local."
/**
* Tell whether the provider room id is a local id.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt
index ecc3eb5224..d03f4c42cf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt
@@ -16,13 +16,13 @@
package org.matrix.android.sdk.api.session.space
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
+import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
data class SpaceHierarchyData(
val rootSummary: RoomSummary,
val children: List,
- val childrenState: List,
+ val childrenState: List,
val nextToken: String? = null
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
index c7a6405014..5d2a9412d1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
@@ -18,10 +18,10 @@ package org.matrix.android.sdk.api.session.space
import android.net.Uri
import androidx.lifecycle.LiveData
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
import org.matrix.android.sdk.api.session.space.peeking.SpacePeekResult
typealias SpaceSummaryQueryParams = RoomSummaryQueryParams
@@ -75,12 +75,12 @@ interface SpaceService {
suggestedOnly: Boolean? = null,
limit: Int? = null,
from: String? = null,
- knownStateList: List? = null
+ knownStateList: List? = null
): SpaceHierarchyData
/**
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
- * @return the [LiveData] of List[SpaceSummary]
+ * @return the [LiveData] of List[RoomSummary]
*/
fun getSpaceSummariesLive(
queryParams: SpaceSummaryQueryParams,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildSummaryEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildSummaryEvent.kt
new file mode 100644
index 0000000000..13aa0336e5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildSummaryEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.api.session.space.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.session.events.model.Content
+
+@JsonClass(generateAdapter = true)
+data class SpaceChildSummaryEvent(
+ @Json(name = "type") val type: String? = null,
+ @Json(name = "state_key") val stateKey: String? = null,
+ @Json(name = "content") val content: Content? = null,
+ @Json(name = "sender") val senderId: String? = null,
+ @Json(name = "origin_server_ts") val originServerTs: Long? = null,
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt
index 0c5465e12a..7075023798 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/UserService.kt
@@ -29,7 +29,7 @@ interface UserService {
/**
* Get a user from a userId.
* @param userId the userId to look for.
- * @return a user with userId or null
+ * @return a user with userId or null if the User is not known yet by the SDK. See [resolveUser] to ensure that a User is retrieved.
*/
fun getUser(userId: String): User?
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index 0b11863864..2693ca474c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -53,6 +53,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo033
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
+import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
@@ -61,7 +62,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
- schemaVersion = 36L,
+ schemaVersion = 37L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
@@ -107,5 +108,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 34) MigrateSessionTo034(realm).perform()
if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
+ if (oldVersion < 37) MigrateSessionTo037(realm).perform()
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LocalRoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LocalRoomSummaryMapper.kt
new file mode 100644
index 0000000000..09cb5985f3
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LocalRoomSummaryMapper.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.internal.database.mapper
+
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
+import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
+import javax.inject.Inject
+
+internal class LocalRoomSummaryMapper @Inject constructor(
+ private val roomSummaryMapper: RoomSummaryMapper,
+) {
+
+ fun map(localRoomSummaryEntity: LocalRoomSummaryEntity): LocalRoomSummary {
+ return LocalRoomSummary(
+ roomId = localRoomSummaryEntity.roomId,
+ roomSummary = localRoomSummaryEntity.roomSummaryEntity?.let { roomSummaryMapper.map(it) },
+ createRoomParams = localRoomSummaryEntity.createRoomParams,
+ replacementRoomId = localRoomSummaryEntity.replacementRoomId,
+ creationState = localRoomSummaryEntity.creationState
+ )
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo037.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo037.kt
new file mode 100644
index 0000000000..cdb0b6c682
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo037.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.internal.database.migration
+
+import io.realm.DynamicRealm
+import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
+import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
+import org.matrix.android.sdk.internal.util.database.RealmMigrator
+
+internal class MigrateSessionTo037(realm: DynamicRealm) : RealmMigrator(realm, 37) {
+
+ override fun doMigrate(realm: DynamicRealm) {
+ realm.schema.get("LocalRoomSummaryEntity")
+ ?.addField(LocalRoomSummaryEntityFields.REPLACEMENT_ROOM_ID, String::class.java)
+ ?.addField(LocalRoomSummaryEntityFields.STATE_STR, String::class.java)
+ ?.transform { obj ->
+ obj.set(LocalRoomSummaryEntityFields.STATE_STR, LocalRoomCreationState.NOT_CREATED.name)
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt
index fd8331e986..a978e3719d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/LocalRoomSummaryEntity.kt
@@ -18,15 +18,24 @@ package org.matrix.android.sdk.internal.database.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
+import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.toJSONString
internal open class LocalRoomSummaryEntity(
@PrimaryKey var roomId: String = "",
var roomSummaryEntity: RoomSummaryEntity? = null,
- private var createRoomParamsStr: String? = null
+ var replacementRoomId: String? = null,
) : RealmObject() {
+ private var stateStr: String = LocalRoomCreationState.NOT_CREATED.name
+ var creationState: LocalRoomCreationState
+ get() = LocalRoomCreationState.valueOf(stateStr)
+ set(value) {
+ stateStr = value.name
+ }
+
+ private var createRoomParamsStr: String? = null
var createRoomParams: CreateRoomParams?
get() {
return CreateRoomParams.fromJson(createRoomParamsStr)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt
index 527350bedc..44730eb75d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LocalRoomSummaryEntityQueries.kt
@@ -22,10 +22,6 @@ import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
-internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery {
- val query = realm.where()
- if (roomId != null) {
- query.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
- }
- return query
+internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String): RealmQuery {
+ return realm.where().equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt
index b180c06e2c..170814d3f2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt
@@ -33,6 +33,11 @@ internal fun ReadReceiptEntity.Companion.whereUserId(realm: Realm, userId: Strin
.equalTo(ReadReceiptEntityFields.USER_ID, userId)
}
+internal fun ReadReceiptEntity.Companion.whereRoomId(realm: Realm, roomId: String): RealmQuery {
+ return realm.where()
+ .equalTo(ReadReceiptEntityFields.ROOM_ID, roomId)
+}
+
internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId: String, userId: String, originServerTs: Double): ReadReceiptEntity {
return ReadReceiptEntity().apply {
this.primaryKey = "${roomId}_$userId"
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt
index b2ab9879df..a93ff42c9e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt
@@ -38,6 +38,7 @@ internal class QueryStringValueProcessor @Inject constructor(
is ContentQueryStringValue -> when (queryStringValue) {
is QueryStringValue.Equals -> equalTo(field, queryStringValue.toRealmValue(), queryStringValue.case.toRealmCase())
is QueryStringValue.Contains -> contains(field, queryStringValue.toRealmValue(), queryStringValue.case.toRealmCase())
+ is QueryStringValue.NotContains -> not().process(field, QueryStringValue.Contains(queryStringValue.string, queryStringValue.case))
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt
index abea2d34cd..262c111b73 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
import org.matrix.android.sdk.api.session.room.location.LocationSharingService
import org.matrix.android.sdk.api.session.room.members.MembershipService
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.relation.RelationService
@@ -82,6 +83,14 @@ internal class DefaultRoom(
return roomSummaryDataSource.getRoomSummary(roomId)
}
+ override fun getLocalRoomSummaryLive(): LiveData> {
+ return roomSummaryDataSource.getLocalRoomSummaryLive(roomId)
+ }
+
+ override fun localRoomSummary(): LocalRoomSummary? {
+ return roomSummaryDataSource.getLocalRoomSummary(roomId)
+ }
+
override fun asSpace(): Space? {
if (roomSummary()?.roomType != RoomType.SPACE) return null
return DefaultSpace(this, roomSummaryDataSource, viaParameterFinder)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
index 989bcaee44..6d72b8ef20 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
@@ -29,10 +29,12 @@ import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
+import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
@@ -106,6 +108,10 @@ internal class DefaultRoomService @Inject constructor(
return roomSummaryDataSource.getRoomSummaryLive(roomId)
}
+ override fun getLocalRoomSummaryLive(roomId: String): LiveData> {
+ return roomSummaryDataSource.getLocalRoomSummaryLive(roomId)
+ }
+
override fun getRoomSummaries(
queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder
@@ -173,7 +179,10 @@ internal class DefaultRoomService @Inject constructor(
}
override suspend fun onRoomDisplayed(roomId: String) {
- updateBreadcrumbsTask.execute(UpdateBreadcrumbsTask.Params(roomId))
+ // Do not add local rooms to the recent rooms list as they should not be known by the server
+ if (!RoomLocalEcho.isLocalEchoId(roomId)) {
+ updateBreadcrumbsTask.execute(UpdateBreadcrumbsTask.Params(roomId))
+ }
}
override suspend fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt
index 02538a5cc3..2245eb8513 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt
@@ -17,38 +17,23 @@
package org.matrix.android.sdk.internal.session.room.create
import com.zhuinden.monarchy.Monarchy
-import io.realm.kotlin.where
import kotlinx.coroutines.TimeoutCancellationException
-import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.query.QueryStringValue
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
-import org.matrix.android.sdk.api.session.events.model.toContent
-import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
+import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
-import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
-import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
-import org.matrix.android.sdk.internal.database.mapper.toEntity
-import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
-import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
-import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
-import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
-import org.matrix.android.sdk.internal.database.query.getOrCreate
+import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase
-import org.matrix.android.sdk.internal.di.UserId
-import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
+import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.task.Task
-import org.matrix.android.sdk.internal.util.awaitTransaction
-import org.matrix.android.sdk.internal.util.time.Clock
-import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -56,94 +41,100 @@ import javax.inject.Inject
* Create a room on the server from a local room.
* The configuration of the local room will be use to configure the new room.
* The potential local room members will also be invited to this new room.
- *
- * A local tombstone event will be created to indicate that the local room has been replacing by the new one.
*/
internal interface CreateRoomFromLocalRoomTask : Task {
data class Params(val localRoomId: String)
}
internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor(
- @UserId private val userId: String,
@SessionDatabase private val monarchy: Monarchy,
private val createRoomTask: CreateRoomTask,
- private val stateEventDataSource: StateEventDataSource,
- private val clock: Clock,
+ private val roomSummaryDataSource: RoomSummaryDataSource,
) : CreateRoomFromLocalRoomTask {
private val realmConfiguration
get() = monarchy.realmConfiguration
override suspend fun execute(params: CreateRoomFromLocalRoomTask.Params): String {
- val replacementRoomId = stateEventDataSource.getStateEvent(params.localRoomId, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
- ?.content.toModel()
- ?.replacementRoomId
+ val localRoomSummary = roomSummaryDataSource.getLocalRoomSummary(params.localRoomId)
+ ?: error("## CreateRoomFromLocalRoomTask - Cannot retrieve LocalRoomSummary with roomId ${params.localRoomId}")
- if (replacementRoomId != null) {
- return replacementRoomId
+ // If a room has already been created for the given local room, return the existing roomId
+ if (localRoomSummary.replacementRoomId != null) {
+ return localRoomSummary.replacementRoomId
}
- var createRoomParams: CreateRoomParams? = null
- var isEncrypted = false
- monarchy.doWithRealm { realm ->
- realm.where()
- .equalTo(LocalRoomSummaryEntityFields.ROOM_ID, params.localRoomId)
- .findFirst()
- ?.let {
- createRoomParams = it.createRoomParams
- isEncrypted = it.roomSummaryEntity?.isEncrypted.orFalse()
- }
+ if (localRoomSummary.createRoomParams != null && localRoomSummary.roomSummary != null) {
+ return createRoom(params.localRoomId, localRoomSummary.roomSummary, localRoomSummary.createRoomParams)
+ } else {
+ error("## CreateRoomFromLocalRoomTask - Invalid LocalRoomSummary: $localRoomSummary")
}
- val roomId = createRoomTask.execute(createRoomParams!!)
+ }
+ /**
+ * Create a room on the server for the given local room.
+ *
+ * @param localRoomId the local room identifier.
+ * @param localRoomSummary the RoomSummary of the local room.
+ * @param createRoomParams the CreateRoomParams object which was used to configure the local room.
+ *
+ * @return the identifier of the created room.
+ */
+ private suspend fun createRoom(localRoomId: String, localRoomSummary: RoomSummary, createRoomParams: CreateRoomParams): String {
+ updateCreationState(localRoomId, LocalRoomCreationState.CREATING)
+ val replacementRoomId = runCatching {
+ createRoomTask.execute(createRoomParams)
+ }.fold(
+ { it },
+ {
+ updateCreationState(localRoomId, LocalRoomCreationState.FAILURE)
+ throw it
+ }
+ )
+ updateReplacementRoomId(localRoomId, replacementRoomId)
+ waitForRoomEvents(replacementRoomId, localRoomSummary)
+ updateCreationState(localRoomId, LocalRoomCreationState.CREATED)
+ return replacementRoomId
+ }
+
+ /**
+ * Wait for all the room events before triggering the created state.
+ *
+ * @param replacementRoomId the identifier of the created room
+ * @param localRoomSummary the RoomSummary of the local room.
+ */
+ private suspend fun waitForRoomEvents(replacementRoomId: String, localRoomSummary: RoomSummary) {
try {
- // Wait for all the room events before triggering the replacement room
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomSummaryEntity::class.java)
- .equalTo(RoomSummaryEntityFields.ROOM_ID, roomId)
- .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams?.invitedUserIds?.size ?: 0)
+ .equalTo(RoomSummaryEntityFields.ROOM_ID, replacementRoomId)
+ .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, localRoomSummary.invitedMembersCount)
}
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
- EventEntity.whereRoomId(realm, roomId)
+ EventEntity.whereRoomId(realm, replacementRoomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY)
}
- if (isEncrypted) {
+ if (localRoomSummary.isEncrypted) {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
- EventEntity.whereRoomId(realm, roomId)
+ EventEntity.whereRoomId(realm, replacementRoomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION)
}
}
} catch (exception: TimeoutCancellationException) {
- throw CreateRoomFailure.CreatedWithTimeout(roomId)
+ updateCreationState(localRoomSummary.roomId, LocalRoomCreationState.FAILURE)
+ throw CreateRoomFailure.CreatedWithTimeout(replacementRoomId)
}
-
- createTombstoneEvent(params, roomId)
- return roomId
}
- /**
- * Create a Tombstone event to indicate that the local room has been replaced by a new one.
- */
- private suspend fun createTombstoneEvent(params: CreateRoomFromLocalRoomTask.Params, roomId: String) {
- val now = clock.epochMillis()
- val event = Event(
- type = EventType.STATE_ROOM_TOMBSTONE,
- senderId = userId,
- originServerTs = now,
- stateKey = "",
- eventId = UUID.randomUUID().toString(),
- content = RoomTombstoneContent(
- replacementRoomId = roomId
- ).toContent()
- )
- monarchy.awaitTransaction { realm ->
- val eventEntity = event.toEntity(params.localRoomId, SendState.SYNCED, now).copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC)
- if (event.stateKey != null && event.type != null && event.eventId != null) {
- CurrentStateEventEntity.getOrCreate(realm, params.localRoomId, event.stateKey, event.type).apply {
- eventId = event.eventId
- root = eventEntity
- }
- }
+ private fun updateCreationState(roomId: String, creationState: LocalRoomCreationState) {
+ monarchy.runTransactionSync { realm ->
+ LocalRoomSummaryEntity.where(realm, roomId).findFirst()?.creationState = creationState
+ }
+ }
+
+ private fun updateReplacementRoomId(localRoomId: String, replacementRoomId: String) {
+ monarchy.runTransactionSync { realm ->
+ LocalRoomSummaryEntity.where(realm, localRoomId).findFirst()?.replacementRoomId = replacementRoomId
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt
index 49951d2da0..a60c7e6a27 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/delete/DeleteLocalRoomTask.kt
@@ -22,12 +22,15 @@ import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
+import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
+import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.deleteOnCascade
import org.matrix.android.sdk.internal.database.query.where
+import org.matrix.android.sdk.internal.database.query.whereInRoom
import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.delete.DeleteLocalRoomTask.Params
@@ -50,6 +53,12 @@ internal class DefaultDeleteLocalRoomTask @Inject constructor(
if (RoomLocalEcho.isLocalEchoId(roomId)) {
monarchy.awaitTransaction { realm ->
Timber.i("## DeleteLocalRoomTask - delete local room id $roomId")
+ ReadReceiptsSummaryEntity.whereInRoom(realm, roomId = roomId).findAll()
+ ?.also { Timber.i("## DeleteLocalRoomTask - ReadReceiptsSummaryEntity - delete ${it.size} entries") }
+ ?.deleteAllFromRealm()
+ ReadReceiptEntity.whereRoomId(realm, roomId = roomId).findAll()
+ ?.also { Timber.i("## DeleteLocalRoomTask - ReadReceiptEntity - delete ${it.size} entries") }
+ ?.deleteAllFromRealm()
RoomMemberSummaryEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - RoomMemberSummaryEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt
index 82fc94df7c..5c4ed8012b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt
@@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.ResultBoundaries
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType
@@ -43,7 +44,9 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.internal.database.mapper.LocalRoomSummaryMapper
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
+import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.findByAlias
@@ -57,6 +60,7 @@ import javax.inject.Inject
internal class RoomSummaryDataSource @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
private val roomSummaryMapper: RoomSummaryMapper,
+ private val localRoomSummaryMapper: LocalRoomSummaryMapper,
private val queryStringValueProcessor: QueryStringValueProcessor,
) {
@@ -95,6 +99,25 @@ internal class RoomSummaryDataSource @Inject constructor(
)
}
+ fun getLocalRoomSummary(roomId: String): LocalRoomSummary? {
+ return monarchy
+ .fetchCopyMap({
+ LocalRoomSummaryEntity.where(it, roomId).findFirst()
+ }, { entity, _ ->
+ localRoomSummaryMapper.map(entity)
+ })
+ }
+
+ fun getLocalRoomSummaryLive(roomId: String): LiveData> {
+ val liveData = monarchy.findAllMappedWithChanges(
+ { realm -> LocalRoomSummaryEntity.where(realm, roomId) },
+ { localRoomSummaryMapper.map(it) }
+ )
+ return Transformations.map(liveData) { results ->
+ results.firstOrNull().toOptional()
+ }
+ }
+
fun getRoomSummariesLive(
queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE
@@ -272,6 +295,7 @@ internal class RoomSummaryDataSource @Inject constructor(
val query = with(queryStringValueProcessor) {
RoomSummaryEntity.where(realm)
.process(RoomSummaryEntityFields.ROOM_ID, QueryStringValue.IsNotEmpty)
+ .process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId)
.process(queryParams.displayName.toDisplayNameField(), queryParams.displayName)
.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt
index d2f1b3202b..cd13b03017 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt
@@ -21,7 +21,6 @@ import androidx.lifecycle.LiveData
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.query.QueryStringValue
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
@@ -45,6 +44,7 @@ import org.matrix.android.sdk.api.session.space.SpaceHierarchyData
import org.matrix.android.sdk.api.session.space.SpaceService
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
+import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent
import org.matrix.android.sdk.api.session.space.peeking.SpacePeekResult
import org.matrix.android.sdk.internal.di.UserId
@@ -128,7 +128,7 @@ internal class DefaultSpaceService @Inject constructor(
suggestedOnly: Boolean?,
limit: Int?,
from: String?,
- knownStateList: List?
+ knownStateList: List?
): SpaceHierarchyData {
val spacesResponse = getSpacesResponse(spaceId, suggestedOnly, limit, from)
val spaceRootResponse = spacesResponse.getRoot(spaceId)
@@ -180,7 +180,7 @@ internal class DefaultSpaceService @Inject constructor(
private fun List?.mapSpaceChildren(
spaceId: String,
spaceRootResponse: SpaceChildSummaryResponse?,
- knownStateList: List?,
+ knownStateList: List?,
) = this?.filterIdIsNot(spaceId)
?.toSpaceChildInfoList(spaceId, spaceRootResponse, knownStateList)
.orEmpty()
@@ -190,7 +190,7 @@ internal class DefaultSpaceService @Inject constructor(
private fun List.toSpaceChildInfoList(
spaceId: String,
rootRoomResponse: SpaceChildSummaryResponse?,
- knownStateList: List?,
+ knownStateList: List?,
) = flatMap { spaceChildSummary ->
(rootRoomResponse?.childrenState ?: knownStateList)
?.filter { it.isChildOf(spaceChildSummary) }
@@ -198,10 +198,14 @@ internal class DefaultSpaceService @Inject constructor(
.orEmpty()
}
- private fun Event.isChildOf(space: SpaceChildSummaryResponse) = stateKey == space.roomId && type == EventType.STATE_SPACE_CHILD
+ private fun SpaceChildSummaryEvent.isChildOf(space: SpaceChildSummaryResponse): Boolean {
+ return stateKey == space.roomId && type == EventType.STATE_SPACE_CHILD
+ }
- private fun Event.toSpaceChildInfo(spaceId: String, summary: SpaceChildSummaryResponse) = content.toModel()?.let { content ->
- createSpaceChildInfo(spaceId, summary, content)
+ private fun SpaceChildSummaryEvent.toSpaceChildInfo(spaceId: String, summary: SpaceChildSummaryResponse): SpaceChildInfo? {
+ return content.toModel()?.let { content ->
+ createSpaceChildInfo(spaceId, summary, content)
+ }
}
private fun createSpaceChildInfo(
@@ -255,7 +259,7 @@ internal class DefaultSpaceService @Inject constructor(
stateKey = QueryStringValue.IsEmpty
)
val powerLevelsContent = powerLevelsEvent?.content?.toModel()
- ?: throw UnsupportedOperationException("Cannot add canonical child, missing powerlevel")
+ ?: throw UnsupportedOperationException("Cannot add canonical child, missing power level")
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt
index e3f8977ac5..0419c5acf1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt
@@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.space
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent
/**
* The fields are the same as those returned by /publicRooms (see spec), with the addition of:
@@ -36,10 +36,11 @@ internal data class SpaceChildSummaryResponse(
*/
@Json(name = "room_type") val roomType: String? = null,
- /** The m.space.child events of the room. For each event, only the following fields are included:
- * type, state_key, content, room_id, sender, with the addition of origin_server_ts.
+ /**
+ * The m.space.child events of the room. For each event, only the following fields are included:
+ * type, state_key, content, sender, and of origin_server_ts.
*/
- @Json(name = "children_state") val childrenState: List? = null,
+ @Json(name = "children_state") val childrenState: List? = null,
/**
* Aliases of the room. May be empty.
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateRoomFromLocalRoomTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateRoomFromLocalRoomTaskTest.kt
index d3732363b5..9e34280437 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateRoomFromLocalRoomTaskTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateRoomFromLocalRoomTaskTest.kt
@@ -22,21 +22,22 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
+import io.mockk.spyk
import io.mockk.unmockkAll
+import io.mockk.verify
+import io.mockk.verifyOrder
import io.realm.kotlin.where
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldBeNull
import org.junit.After
import org.junit.Before
import org.junit.Test
-import org.matrix.android.sdk.api.query.QueryStringValue
-import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.session.events.model.EventType
-import org.matrix.android.sdk.api.session.events.model.toContent
-import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
-import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
@@ -44,29 +45,24 @@ import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate
-import org.matrix.android.sdk.internal.util.time.DefaultClock
import org.matrix.android.sdk.test.fakes.FakeMonarchy
-import org.matrix.android.sdk.test.fakes.FakeStateEventDataSource
+import org.matrix.android.sdk.test.fakes.FakeRoomSummaryDataSource
private const val A_LOCAL_ROOM_ID = "local.a-local-room-id"
private const val AN_EXISTING_ROOM_ID = "an-existing-room-id"
private const val A_ROOM_ID = "a-room-id"
-private const val MY_USER_ID = "my-user-id"
@ExperimentalCoroutinesApi
internal class DefaultCreateRoomFromLocalRoomTaskTest {
private val fakeMonarchy = FakeMonarchy()
- private val clock = DefaultClock()
private val createRoomTask = mockk()
- private val fakeStateEventDataSource = FakeStateEventDataSource()
+ private val fakeRoomSummaryDataSource = FakeRoomSummaryDataSource()
private val defaultCreateRoomFromLocalRoomTask = DefaultCreateRoomFromLocalRoomTask(
- userId = MY_USER_ID,
monarchy = fakeMonarchy.instance,
createRoomTask = createRoomTask,
- stateEventDataSource = fakeStateEventDataSource.instance,
- clock = clock
+ roomSummaryDataSource = fakeRoomSummaryDataSource.instance,
)
@Before
@@ -91,13 +87,12 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
@Test
fun `given a local room id when execute then the existing room id is kept`() = runTest {
// Given
- givenATombstoneEvent(
- Event(
- roomId = A_LOCAL_ROOM_ID,
- type = EventType.STATE_ROOM_TOMBSTONE,
- stateKey = "",
- content = RoomTombstoneContent(replacementRoomId = AN_EXISTING_ROOM_ID).toContent()
- )
+ val aCreateRoomParams = mockk(relaxed = true)
+ givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aCreationState = LocalRoomCreationState.CREATED, aReplacementRoomId = AN_EXISTING_ROOM_ID)
+ val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(
+ aCreateRoomParams = aCreateRoomParams,
+ aCreationState = LocalRoomCreationState.CREATED,
+ aReplacementRoomId = AN_EXISTING_ROOM_ID
)
// When
@@ -105,20 +100,18 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
val result = defaultCreateRoomFromLocalRoomTask.execute(params)
// Then
- verifyTombstoneEvent(AN_EXISTING_ROOM_ID)
+ fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
result shouldBeEqualTo AN_EXISTING_ROOM_ID
+ aLocalRoomSummaryEntity.replacementRoomId shouldBeEqualTo AN_EXISTING_ROOM_ID
+ aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.CREATED
}
@Test
fun `given a local room id when execute then it is correctly executed`() = runTest {
// Given
- val aCreateRoomParams = mockk()
- val aLocalRoomSummaryEntity = mockk {
- every { roomSummaryEntity } returns mockk(relaxed = true)
- every { createRoomParams } returns aCreateRoomParams
- }
- givenATombstoneEvent(null)
- givenALocalRoomSummaryEntity(aLocalRoomSummaryEntity)
+ val aCreateRoomParams = mockk(relaxed = true)
+ givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
+ val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
coEvery { createRoomTask.execute(any()) } returns A_ROOM_ID
@@ -127,32 +120,84 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
val result = defaultCreateRoomFromLocalRoomTask.execute(params)
// Then
- verifyTombstoneEvent(null)
+ fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
// CreateRoomTask has been called with the initial CreateRoomParams
coVerify { createRoomTask.execute(aCreateRoomParams) }
// The resulting roomId matches the roomId returned by the createRoomTask
result shouldBeEqualTo A_ROOM_ID
- // A tombstone state event has been created
- coVerify { CurrentStateEventEntity.getOrCreate(realm = any(), roomId = A_LOCAL_ROOM_ID, stateKey = any(), type = EventType.STATE_ROOM_TOMBSTONE) }
+ // The room creation state has correctly been updated
+ verifyOrder {
+ aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATING
+ aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATED
+ }
+ // The local room summary has been updated with the created room id
+ verify { aLocalRoomSummaryEntity.replacementRoomId = A_ROOM_ID }
+ aLocalRoomSummaryEntity.replacementRoomId shouldBeEqualTo A_ROOM_ID
+ aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.CREATED
}
- private fun givenATombstoneEvent(event: Event?) {
- fakeStateEventDataSource.givenGetStateEventReturns(event)
+ @Test
+ fun `given a local room id when execute with an exception then the creation state is correctly updated`() = runTest {
+ // Given
+ val aCreateRoomParams = mockk(relaxed = true)
+ givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
+ val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
+
+ coEvery { createRoomTask.execute(any()) }.throws(mockk())
+
+ // When
+ val params = CreateRoomFromLocalRoomTask.Params(A_LOCAL_ROOM_ID)
+ tryOrNull { defaultCreateRoomFromLocalRoomTask.execute(params) }
+
+ // Then
+ fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
+ // CreateRoomTask has been called with the initial CreateRoomParams
+ coVerify { createRoomTask.execute(aCreateRoomParams) }
+ // The room creation state has correctly been updated
+ verifyOrder {
+ aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATING
+ aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.FAILURE
+ }
+ // The local room summary has been updated with the created room id
+ aLocalRoomSummaryEntity.replacementRoomId.shouldBeNull()
+ aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.FAILURE
}
- private fun givenALocalRoomSummaryEntity(localRoomSummaryEntity: LocalRoomSummaryEntity) {
+ private fun givenALocalRoomSummary(
+ aCreateRoomParams: CreateRoomParams,
+ aCreationState: LocalRoomCreationState = LocalRoomCreationState.NOT_CREATED,
+ aReplacementRoomId: String? = null
+ ): LocalRoomSummary {
+ val aLocalRoomSummary = LocalRoomSummary(
+ roomId = A_LOCAL_ROOM_ID,
+ roomSummary = mockk(relaxed = true),
+ createRoomParams = aCreateRoomParams,
+ creationState = aCreationState,
+ replacementRoomId = aReplacementRoomId,
+ )
+ fakeRoomSummaryDataSource.givenGetLocalRoomSummaryReturns(A_LOCAL_ROOM_ID, aLocalRoomSummary)
+ return aLocalRoomSummary
+ }
+
+ private fun givenALocalRoomSummaryEntity(
+ aCreateRoomParams: CreateRoomParams,
+ aCreationState: LocalRoomCreationState = LocalRoomCreationState.NOT_CREATED,
+ aReplacementRoomId: String? = null
+ ): LocalRoomSummaryEntity {
+ val aLocalRoomSummaryEntity = spyk(LocalRoomSummaryEntity(
+ roomId = A_LOCAL_ROOM_ID,
+ roomSummaryEntity = mockk(relaxed = true),
+ replacementRoomId = aReplacementRoomId,
+ ).apply {
+ createRoomParams = aCreateRoomParams
+ creationState = aCreationState
+ })
every {
fakeMonarchy.fakeRealm.instance
.where()
.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, A_LOCAL_ROOM_ID)
.findFirst()
- } returns localRoomSummaryEntity
- }
-
- private fun verifyTombstoneEvent(expectedRoomId: String?) {
- fakeStateEventDataSource.verifyGetStateEvent(A_LOCAL_ROOM_ID, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
- fakeStateEventDataSource.instance.getStateEvent(A_LOCAL_ROOM_ID, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
- ?.content.toModel()
- ?.replacementRoomId shouldBeEqualTo expectedRoomId
+ } returns aLocalRoomSummaryEntity
+ return aLocalRoomSummaryEntity
}
}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt
index 2d501f12af..93999458c6 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt
@@ -47,6 +47,11 @@ internal class FakeMonarchy {
} coAnswers {
firstArg().doWithRealm(fakeRealm.instance)
}
+ coEvery {
+ instance.runTransactionSync(any())
+ } coAnswers {
+ firstArg().execute(fakeRealm.instance)
+ }
every { instance.realmConfiguration } returns mockk()
}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomSummaryDataSource.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomSummaryDataSource.kt
new file mode 100644
index 0000000000..c7b70a3ad5
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomSummaryDataSource.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
+ *
+ * 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 org.matrix.android.sdk.test.fakes
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
+import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
+
+internal class FakeRoomSummaryDataSource {
+
+ val instance: RoomSummaryDataSource = mockk()
+
+ fun givenGetLocalRoomSummaryReturns(roomId: String?, localRoomSummary: LocalRoomSummary?) {
+ every { instance.getLocalRoomSummary(roomId = roomId ?: any()) } returns localRoomSummary
+ }
+
+ fun verifyGetLocalRoomSummary(roomId: String) {
+ verify { instance.getLocalRoomSummary(roomId) }
+ }
+}
diff --git a/tools/emojis/emoji_picker_datasource_formatted.json b/tools/emojis/emoji_picker_datasource_formatted.json
index 41465a442f..c00bd10371 100644
--- a/tools/emojis/emoji_picker_datasource_formatted.json
+++ b/tools/emojis/emoji_picker_datasource_formatted.json
@@ -54,6 +54,7 @@
"grimacing-face",
"face-exhaling",
"lying-face",
+ "shaking-face",
"relieved-face",
"pensive-face",
"sleepy-face",
@@ -104,7 +105,7 @@
"tired-face",
"yawning-face",
"face-with-steam-from-nose",
- "pouting-face",
+ "enraged-face",
"angry-face",
"face-with-symbols-on-mouth",
"smiling-face-with-horns",
@@ -131,7 +132,6 @@
"seenoevil-monkey",
"hearnoevil-monkey",
"speaknoevil-monkey",
- "kiss-mark",
"love-letter",
"heart-with-arrow",
"heart-with-ribbon",
@@ -146,14 +146,18 @@
"heart-on-fire",
"mending-heart",
"red-heart",
+ "pink-heart",
"orange-heart",
"yellow-heart",
"green-heart",
"blue-heart",
+ "light-blue-heart",
"purple-heart",
"brown-heart",
"black-heart",
+ "grey-heart",
"white-heart",
+ "kiss-mark",
"hundred-points",
"anger-symbol",
"collision",
@@ -161,7 +165,6 @@
"sweat-droplets",
"dashing-away",
"hole",
- "bomb",
"speech-balloon",
"eye-in-speech-bubble",
"left-speech-bubble",
@@ -183,6 +186,8 @@
"leftwards-hand",
"palm-down-hand",
"palm-up-hand",
+ "leftwards-pushing-hand",
+ "rightwards-pushing-hand",
"ok-hand",
"pinched-fingers",
"pinching-hand",
@@ -561,6 +566,8 @@
"tiger",
"leopard",
"horse-face",
+ "moose",
+ "donkey",
"horse",
"unicorn",
"zebra",
@@ -623,6 +630,9 @@
"flamingo",
"peacock",
"parrot",
+ "wing",
+ "black-bird",
+ "goose",
"frog",
"crocodile",
"turtle",
@@ -643,6 +653,7 @@
"octopus",
"spiral-shell",
"coral",
+ "jellyfish",
"snail",
"butterfly",
"bug",
@@ -670,6 +681,7 @@
"sunflower",
"blossom",
"tulip",
+ "hyacinth",
"seedling",
"potted-plant",
"evergreen-tree",
@@ -684,7 +696,8 @@
"fallen-leaf",
"leaf-fluttering-in-wind",
"empty-nest",
- "nest-with-eggs"
+ "nest-with-eggs",
+ "mushroom"
]
},
{
@@ -722,10 +735,11 @@
"broccoli",
"garlic",
"onion",
- "mushroom",
"peanuts",
"beans",
"chestnut",
+ "ginger-root",
+ "pea-pod",
"bread",
"croissant",
"baguette-bread",
@@ -1110,11 +1124,10 @@
"bullseye",
"yoyo",
"kite",
+ "water-pistol",
"pool-8-ball",
"crystal-ball",
"magic-wand",
- "nazar-amulet",
- "hamsa",
"video-game",
"joystick",
"slot-machine",
@@ -1165,6 +1178,7 @@
"shorts",
"bikini",
"womans-clothes",
+ "folding-hand-fan",
"purse",
"handbag",
"clutch-bag",
@@ -1179,6 +1193,7 @@
"womans-sandal",
"ballet-shoes",
"womans-boot",
+ "hair-pick",
"crown",
"womans-hat",
"top-hat",
@@ -1217,6 +1232,8 @@
"banjo",
"drum",
"long-drum",
+ "maracas",
+ "flute",
"mobile-phone",
"mobile-phone-with-arrow",
"telephone",
@@ -1336,7 +1353,7 @@
"hammer-and-wrench",
"dagger",
"crossed-swords",
- "water-pistol",
+ "bomb",
"boomerang",
"bow-and-arrow",
"shield",
@@ -1397,6 +1414,8 @@
"coffin",
"headstone",
"funeral-urn",
+ "nazar-amulet",
+ "hamsa",
"moai",
"placard",
"identification-card"
@@ -1465,6 +1484,7 @@
"peace-symbol",
"menorah",
"dotted-sixpointed-star",
+ "khanda",
"aries",
"taurus",
"gemini",
@@ -1500,6 +1520,7 @@
"dim-button",
"bright-button",
"antenna-bars",
+ "wireless",
"vibration-mode",
"mobile-phone-off",
"female-sign",
@@ -2050,7 +2071,7 @@
]
},
"melting-face": {
- "a": "⊛ Melting Face",
+ "a": "Melting Face",
"b": "1FAE0",
"j": [
"disappear",
@@ -2345,7 +2366,7 @@
]
},
"face-with-open-eyes-and-hand-over-mouth": {
- "a": "⊛ Face with Open Eyes and Hand over Mouth",
+ "a": "Face with Open Eyes and Hand over Mouth",
"b": "1FAE2",
"j": [
"amazement",
@@ -2360,7 +2381,7 @@
]
},
"face-with-peeking-eye": {
- "a": "⊛ Face with Peeking Eye",
+ "a": "Face with Peeking Eye",
"b": "1FAE3",
"j": [
"captivated",
@@ -2368,7 +2389,8 @@
"stare",
"scared",
"frightening",
- "embarrassing"
+ "embarrassing",
+ "shy"
]
},
"shushing-face": {
@@ -2393,10 +2415,10 @@
]
},
"saluting-face": {
- "a": "⊛ Saluting Face",
+ "a": "Saluting Face",
"b": "1FAE1",
"j": [
- "ok",
+ "OK",
"salute",
"sunny",
"troops",
@@ -2469,7 +2491,7 @@
]
},
"dotted-line-face": {
- "a": "⊛ Dotted Line Face",
+ "a": "Dotted Line Face",
"b": "1FAE5",
"j": [
"depressed",
@@ -2569,6 +2591,17 @@
"pinocchio"
]
},
+ "shaking-face": {
+ "a": "⊛ Shaking Face",
+ "b": "1FAE8",
+ "j": [
+ "earthquake",
+ "face",
+ "shaking",
+ "shock",
+ "vibrate"
+ ]
+ },
"relieved-face": {
"a": "Relieved Face",
"b": "1F60C",
@@ -2598,6 +2631,7 @@
"b": "1F62A",
"j": [
"face",
+ "good night",
"sleep",
"tired",
"rest",
@@ -2617,11 +2651,13 @@
"b": "1F634",
"j": [
"face",
+ "good night",
"sleep",
- "zzz",
+ "ZZZ",
"tired",
"sleepy",
- "night"
+ "night",
+ "zzz"
]
},
"face-with-medical-mask": {
@@ -2851,9 +2887,10 @@
"a": "Face with Monocle",
"b": "1F9D0",
"j": [
+ "face",
+ "monocle",
"stuffy",
- "wealthy",
- "face"
+ "wealthy"
]
},
"confused-face": {
@@ -2871,7 +2908,7 @@
]
},
"face-with-diagonal-mouth": {
- "a": "⊛ Face with Diagonal Mouth",
+ "a": "Face with Diagonal Mouth",
"b": "1FAE4",
"j": [
"disappointed",
@@ -2980,7 +3017,7 @@
]
},
"face-holding-back-tears": {
- "a": "⊛ Face Holding Back Tears",
+ "a": "Face Holding Back Tears",
"b": "1F979",
"j": [
"angry",
@@ -3191,16 +3228,18 @@
"pride"
]
},
- "pouting-face": {
- "a": "Pouting Face",
+ "enraged-face": {
+ "a": "Enraged Face",
"b": "1F621",
"j": [
"angry",
+ "enraged",
"face",
"mad",
"pouting",
"rage",
"red",
+ "pouting_face",
"hate",
"despise"
]
@@ -3578,19 +3617,6 @@
"omg"
]
},
- "kiss-mark": {
- "a": "Kiss Mark",
- "b": "1F48B",
- "j": [
- "kiss",
- "lips",
- "face",
- "love",
- "like",
- "affection",
- "valentines"
- ]
- },
"love-letter": {
"a": "Love Letter",
"b": "1F48C",
@@ -3764,6 +3790,17 @@
"valentines"
]
},
+ "pink-heart": {
+ "a": "⊛ Pink Heart",
+ "b": "1FA77",
+ "j": [
+ "cute",
+ "heart",
+ "like",
+ "love",
+ "pink"
+ ]
+ },
"orange-heart": {
"a": "Orange Heart",
"b": "1F9E1",
@@ -3808,6 +3845,17 @@
"valentines"
]
},
+ "light-blue-heart": {
+ "a": "⊛ Light Blue Heart",
+ "b": "1FA75",
+ "j": [
+ "cyan",
+ "heart",
+ "light blue",
+ "light blue heart",
+ "teal"
+ ]
+ },
"purple-heart": {
"a": "Purple Heart",
"b": "1F49C",
@@ -3837,6 +3885,17 @@
"wicked"
]
},
+ "grey-heart": {
+ "a": "⊛ Grey Heart",
+ "b": "1FA76",
+ "j": [
+ "gray",
+ "grey heart",
+ "heart",
+ "silver",
+ "slate"
+ ]
+ },
"white-heart": {
"a": "White Heart",
"b": "1F90D",
@@ -3846,6 +3905,19 @@
"pure"
]
},
+ "kiss-mark": {
+ "a": "Kiss Mark",
+ "b": "1F48B",
+ "j": [
+ "kiss",
+ "lips",
+ "face",
+ "love",
+ "like",
+ "affection",
+ "valentines"
+ ]
+ },
"hundred-points": {
"a": "Hundred Points",
"b": "1F4AF",
@@ -3930,17 +4002,6 @@
"embarrassing"
]
},
- "bomb": {
- "a": "Bomb",
- "b": "1F4A3",
- "j": [
- "comic",
- "boom",
- "explode",
- "explosion",
- "terrorism"
- ]
- },
"speech-balloon": {
"a": "Speech Balloon",
"b": "1F4AC",
@@ -3960,8 +4021,10 @@
"a": "Eye in Speech Bubble",
"b": "1F441-FE0F-200D-1F5E8-FE0F",
"j": [
+ "balloon",
+ "bubble",
"eye",
- "speech bubble",
+ "speech",
"witness",
"info"
]
@@ -3970,6 +4033,8 @@
"a": "Left Speech Bubble",
"b": "1F5E8",
"j": [
+ "balloon",
+ "bubble",
"dialog",
"speech",
"words",
@@ -4010,7 +4075,9 @@
"b": "1F4A4",
"j": [
"comic",
+ "good night",
"sleep",
+ "ZZZ",
"sleepy",
"tired",
"dream"
@@ -4080,7 +4147,7 @@
]
},
"rightwards-hand": {
- "a": "⊛ Rightwards Hand",
+ "a": "Rightwards Hand",
"b": "1FAF1",
"j": [
"hand",
@@ -4091,7 +4158,7 @@
]
},
"leftwards-hand": {
- "a": "⊛ Leftwards Hand",
+ "a": "Leftwards Hand",
"b": "1FAF2",
"j": [
"hand",
@@ -4102,7 +4169,7 @@
]
},
"palm-down-hand": {
- "a": "⊛ Palm Down Hand",
+ "a": "Palm Down Hand",
"b": "1FAF3",
"j": [
"dismiss",
@@ -4112,7 +4179,7 @@
]
},
"palm-up-hand": {
- "a": "⊛ Palm Up Hand",
+ "a": "Palm Up Hand",
"b": "1FAF4",
"j": [
"beckon",
@@ -4123,6 +4190,32 @@
"demand"
]
},
+ "leftwards-pushing-hand": {
+ "a": "⊛ Leftwards Pushing Hand",
+ "b": "1FAF7",
+ "j": [
+ "high five",
+ "leftward",
+ "leftwards pushing hand",
+ "push",
+ "refuse",
+ "stop",
+ "wait"
+ ]
+ },
+ "rightwards-pushing-hand": {
+ "a": "⊛ Rightwards Pushing Hand",
+ "b": "1FAF8",
+ "j": [
+ "high five",
+ "push",
+ "refuse",
+ "rightward",
+ "rightwards pushing hand",
+ "stop",
+ "wait"
+ ]
+ },
"ok-hand": {
"a": "Ok Hand",
"b": "1F44C",
@@ -4186,7 +4279,7 @@
]
},
"hand-with-index-finger-and-thumb-crossed": {
- "a": "⊛ Hand with Index Finger and Thumb Crossed",
+ "a": "Hand with Index Finger and Thumb Crossed",
"b": "1FAF0",
"j": [
"expensive",
@@ -4228,6 +4321,8 @@
"j": [
"call",
"hand",
+ "hang loose",
+ "Shaka",
"hands",
"gesture",
"shaka"
@@ -4313,7 +4408,7 @@
]
},
"index-pointing-at-the-viewer": {
- "a": "⊛ Index Pointing at the Viewer",
+ "a": "Index Pointing at the Viewer",
"b": "1FAF5",
"j": [
"point",
@@ -4429,7 +4524,7 @@
]
},
"heart-hands": {
- "a": "⊛ Heart Hands",
+ "a": "Heart Hands",
"b": "1FAF6",
"j": [
"love",
@@ -4686,7 +4781,7 @@
]
},
"biting-lip": {
- "a": "⊛ Biting Lip",
+ "a": "Biting Lip",
"b": "1FAE6",
"j": [
"anxious",
@@ -6088,7 +6183,7 @@
]
},
"person-with-crown": {
- "a": "⊛ Person with Crown",
+ "a": "Person with Crown",
"b": "1FAC5",
"j": [
"monarch",
@@ -6262,7 +6357,7 @@
]
},
"pregnant-man": {
- "a": "⊛ Pregnant Man",
+ "a": "Pregnant Man",
"b": "1FAC3",
"j": [
"belly",
@@ -6273,7 +6368,7 @@
]
},
"pregnant-person": {
- "a": "⊛ Pregnant Person",
+ "a": "Pregnant Person",
"b": "1FAC4",
"j": [
"belly",
@@ -6669,7 +6764,7 @@
]
},
"troll": {
- "a": "⊛ Troll",
+ "a": "Troll",
"b": "1F9CC",
"j": [
"fairy tale",
@@ -7633,6 +7728,7 @@
"a": "Person in Bed",
"b": "1F6CC",
"j": [
+ "good night",
"hotel",
"sleep",
"bed",
@@ -8514,6 +8610,30 @@
"nature"
]
},
+ "moose": {
+ "a": "⊛ Moose",
+ "b": "1FACE",
+ "j": [
+ "animal",
+ "antlers",
+ "elk",
+ "mammal",
+ "moose"
+ ]
+ },
+ "donkey": {
+ "a": "⊛ Donkey",
+ "b": "1FACF",
+ "j": [
+ "animal",
+ "ass",
+ "burro",
+ "donkey",
+ "mammal",
+ "mule",
+ "stubborn"
+ ]
+ },
"horse": {
"a": "Horse",
"b": "1F40E",
@@ -9180,6 +9300,40 @@
"nature"
]
},
+ "wing": {
+ "a": "⊛ Wing",
+ "b": "1FABD",
+ "j": [
+ "angelic",
+ "aviation",
+ "bird",
+ "flying",
+ "mythology",
+ "wing"
+ ]
+ },
+ "black-bird": {
+ "a": "⊛ Black Bird",
+ "b": "1F426-200D-2B1B",
+ "j": [
+ "bird",
+ "black",
+ "crow",
+ "raven",
+ "rook"
+ ]
+ },
+ "goose": {
+ "a": "⊛ Goose",
+ "b": "1FABF",
+ "j": [
+ "bird",
+ "fowl",
+ "goose",
+ "honk",
+ "silly"
+ ]
+ },
"frog": {
"a": "Frog",
"b": "1F438",
@@ -9410,7 +9564,7 @@
]
},
"coral": {
- "a": "⊛ Coral",
+ "a": "Coral",
"b": "1FAB8",
"j": [
"ocean",
@@ -9418,6 +9572,19 @@
"sea"
]
},
+ "jellyfish": {
+ "a": "⊛ Jellyfish",
+ "b": "1FABC",
+ "j": [
+ "burn",
+ "invertebrate",
+ "jelly",
+ "jellyfish",
+ "marine",
+ "ouch",
+ "stinger"
+ ]
+ },
"snail": {
"a": "Snail",
"b": "1F40C",
@@ -9621,7 +9788,7 @@
]
},
"lotus": {
- "a": "⊛ Lotus",
+ "a": "Lotus",
"b": "1FAB7",
"j": [
"Buddhism",
@@ -9662,7 +9829,8 @@
"flower",
"wilted",
"plant",
- "nature"
+ "nature",
+ "rose"
]
},
"hibiscus": {
@@ -9709,6 +9877,18 @@
"spring"
]
},
+ "hyacinth": {
+ "a": "⊛ Hyacinth",
+ "b": "1FABB",
+ "j": [
+ "bluebonnet",
+ "flower",
+ "hyacinth",
+ "lavender",
+ "lupine",
+ "snapdragon"
+ ]
+ },
"seedling": {
"a": "Seedling",
"b": "1F331",
@@ -9873,7 +10053,7 @@
]
},
"empty-nest": {
- "a": "⊛ Empty Nest",
+ "a": "Empty Nest",
"b": "1FAB9",
"j": [
"nesting",
@@ -9881,13 +10061,22 @@
]
},
"nest-with-eggs": {
- "a": "⊛ Nest with Eggs",
+ "a": "Nest with Eggs",
"b": "1FABA",
"j": [
"nesting",
"bird"
]
},
+ "mushroom": {
+ "a": "Mushroom",
+ "b": "1F344",
+ "j": [
+ "toadstool",
+ "plant",
+ "vegetable"
+ ]
+ },
"grapes": {
"a": "Grapes",
"b": "1F347",
@@ -10199,15 +10388,6 @@
"spice"
]
},
- "mushroom": {
- "a": "Mushroom",
- "b": "1F344",
- "j": [
- "toadstool",
- "plant",
- "vegetable"
- ]
- },
"peanuts": {
"a": "Peanuts",
"b": "1F95C",
@@ -10219,7 +10399,7 @@
]
},
"beans": {
- "a": "⊛ Beans",
+ "a": "Beans",
"b": "1FAD8",
"j": [
"food",
@@ -10236,6 +10416,28 @@
"squirrel"
]
},
+ "ginger-root": {
+ "a": "⊛ Ginger Root",
+ "b": "1FADA",
+ "j": [
+ "beer",
+ "ginger root",
+ "root",
+ "spice"
+ ]
+ },
+ "pea-pod": {
+ "a": "⊛ Pea Pod",
+ "b": "1FADB",
+ "j": [
+ "beans",
+ "edamame",
+ "legume",
+ "pea",
+ "pod",
+ "vegetable"
+ ]
+ },
"bread": {
"a": "Bread",
"b": "1F35E",
@@ -11080,7 +11282,8 @@
"tea",
"caffeine",
"latte",
- "espresso"
+ "espresso",
+ "mug"
]
},
"teapot": {
@@ -11255,7 +11458,7 @@
]
},
"pouring-liquid": {
- "a": "⊛ Pouring Liquid",
+ "a": "Pouring Liquid",
"b": "1FAD7",
"j": [
"drink",
@@ -11384,7 +11587,7 @@
]
},
"jar": {
- "a": "⊛ Jar",
+ "a": "Jar",
"b": "1FAD9",
"j": [
"condiment",
@@ -12075,7 +12278,7 @@
]
},
"playground-slide": {
- "a": "⊛ Playground Slide",
+ "a": "Playground Slide",
"b": "1F6DD",
"j": [
"amusement park",
@@ -12606,7 +12809,7 @@
]
},
"wheel": {
- "a": "⊛ Wheel",
+ "a": "Wheel",
"b": "1F6DE",
"j": [
"circle",
@@ -12688,7 +12891,7 @@
]
},
"ring-buoy": {
- "a": "⊛ Ring Buoy",
+ "a": "Ring Buoy",
"b": "1F6DF",
"j": [
"float",
@@ -14683,6 +14886,20 @@
"wind"
]
},
+ "water-pistol": {
+ "a": "Water Pistol",
+ "b": "1F52B",
+ "j": [
+ "gun",
+ "handgun",
+ "pistol",
+ "revolver",
+ "tool",
+ "water",
+ "weapon",
+ "violence"
+ ]
+ },
"pool-8-ball": {
"a": "Pool 8 Ball",
"b": "1F3B1",
@@ -14726,30 +14943,6 @@
"power"
]
},
- "nazar-amulet": {
- "a": "Nazar Amulet",
- "b": "1F9FF",
- "j": [
- "bead",
- "charm",
- "evil-eye",
- "nazar",
- "talisman"
- ]
- },
- "hamsa": {
- "a": "⊛ Hamsa",
- "b": "1FAAC",
- "j": [
- "amulet",
- "Fatima",
- "hand",
- "Mary",
- "Miriam",
- "protection",
- "religion"
- ]
- },
"video-game": {
"a": "Video Game",
"b": "1F3AE",
@@ -14831,7 +15024,7 @@
]
},
"mirror-ball": {
- "a": "⊛ Mirror Ball",
+ "a": "Mirror Ball",
"b": "1FAA9",
"j": [
"dance",
@@ -15252,6 +15445,19 @@
"female"
]
},
+ "folding-hand-fan": {
+ "a": "⊛ Folding Hand Fan",
+ "b": "1FAAD",
+ "j": [
+ "cooling",
+ "dance",
+ "fan",
+ "flutter",
+ "folding hand fan",
+ "hot",
+ "shy"
+ ]
+ },
"purse": {
"a": "Purse",
"b": "1F45B",
@@ -15426,6 +15632,16 @@
"fashion"
]
},
+ "hair-pick": {
+ "a": "⊛ Hair Pick",
+ "b": "1FAAE",
+ "j": [
+ "Afro",
+ "comb",
+ "hair",
+ "pick"
+ ]
+ },
"crown": {
"a": "Crown",
"b": "1F451",
@@ -15864,6 +16080,30 @@
"music"
]
},
+ "maracas": {
+ "a": "⊛ Maracas",
+ "b": "1FA87",
+ "j": [
+ "instrument",
+ "maracas",
+ "music",
+ "percussion",
+ "rattle",
+ "shake"
+ ]
+ },
+ "flute": {
+ "a": "⊛ Flute",
+ "b": "1FA88",
+ "j": [
+ "fife",
+ "flute",
+ "music",
+ "pipe",
+ "recorder",
+ "woodwind"
+ ]
+ },
"mobile-phone": {
"a": "Mobile Phone",
"b": "1F4F1",
@@ -15941,7 +16181,7 @@
]
},
"low-battery": {
- "a": "⊛ Low Battery",
+ "a": "Low Battery",
"b": "1FAAB",
"j": [
"electronic",
@@ -16054,7 +16294,7 @@
"a": "Optical Disk",
"b": "1F4BF",
"j": [
- "cd",
+ "CD",
"computer",
"disk",
"optical",
@@ -16068,9 +16308,10 @@
"a": "Dvd",
"b": "1F4C0",
"j": [
- "blu-ray",
+ "Blu-ray",
"computer",
"disk",
+ "DVD",
"optical",
"cd",
"disc"
@@ -17258,18 +17499,15 @@
"weapon"
]
},
- "water-pistol": {
- "a": "Water Pistol",
- "b": "1F52B",
+ "bomb": {
+ "a": "Bomb",
+ "b": "1F4A3",
"j": [
- "gun",
- "handgun",
- "pistol",
- "revolver",
- "tool",
- "water",
- "weapon",
- "violence"
+ "comic",
+ "boom",
+ "explode",
+ "explosion",
+ "terrorism"
]
},
"boomerang": {
@@ -17584,7 +17822,7 @@
]
},
"crutch": {
- "a": "⊛ Crutch",
+ "a": "Crutch",
"b": "1FA7C",
"j": [
"cane",
@@ -17607,7 +17845,7 @@
]
},
"xray": {
- "a": "⊛ X-Ray",
+ "a": "X-Ray",
"b": "1FA7B",
"j": [
"bones",
@@ -17814,7 +18052,7 @@
]
},
"bubbles": {
- "a": "⊛ Bubbles",
+ "a": "Bubbles",
"b": "1FAE7",
"j": [
"burp",
@@ -17917,6 +18155,30 @@
"rip"
]
},
+ "nazar-amulet": {
+ "a": "Nazar Amulet",
+ "b": "1F9FF",
+ "j": [
+ "bead",
+ "charm",
+ "evil-eye",
+ "nazar",
+ "talisman"
+ ]
+ },
+ "hamsa": {
+ "a": "Hamsa",
+ "b": "1FAAC",
+ "j": [
+ "amulet",
+ "Fatima",
+ "hand",
+ "Mary",
+ "Miriam",
+ "protection",
+ "religion"
+ ]
+ },
"moai": {
"a": "Moai",
"b": "1F5FF",
@@ -17940,7 +18202,7 @@
]
},
"identification-card": {
- "a": "⊛ Identification Card",
+ "a": "Identification Card",
"b": "1FAAA",
"j": [
"credentials",
@@ -17954,7 +18216,7 @@
"a": "Atm Sign",
"b": "1F3E7",
"j": [
- "atm",
+ "ATM",
"ATM sign",
"automated",
"bank",
@@ -18006,13 +18268,15 @@
"a": "Men’S Room",
"b": "1F6B9",
"j": [
+ "bathroom",
"lavatory",
"man",
"men’s room",
"restroom",
- "wc",
- "men_s_room",
"toilet",
+ "WC",
+ "men_s_room",
+ "wc",
"blue-square",
"gender",
"male"
@@ -18022,15 +18286,16 @@
"a": "Women’S Room",
"b": "1F6BA",
"j": [
+ "bathroom",
"lavatory",
"restroom",
- "wc",
+ "toilet",
+ "WC",
"woman",
"women’s room",
"women_s_room",
"purple-square",
"female",
- "toilet",
"loo",
"gender"
]
@@ -18039,10 +18304,11 @@
"a": "Restroom",
"b": "1F6BB",
"j": [
+ "bathroom",
"lavatory",
+ "toilet",
"WC",
"blue-square",
- "toilet",
"refresh",
"wc",
"gender"
@@ -18062,12 +18328,13 @@
"a": "Water Closet",
"b": "1F6BE",
"j": [
+ "bathroom",
"closet",
"lavatory",
"restroom",
- "water",
- "wc",
"toilet",
+ "water",
+ "WC",
"blue-square"
]
},
@@ -18504,8 +18771,7 @@
"b": "1F519",
"j": [
"arrow",
- "back",
- "BACK arrow",
+ "BACK",
"words",
"return"
]
@@ -18515,8 +18781,7 @@
"b": "1F51A",
"j": [
"arrow",
- "end",
- "END arrow",
+ "END",
"words"
]
},
@@ -18526,8 +18791,8 @@
"j": [
"arrow",
"mark",
- "on",
- "ON! arrow",
+ "ON",
+ "ON!",
"words"
]
},
@@ -18536,8 +18801,7 @@
"b": "1F51C",
"j": [
"arrow",
- "soon",
- "SOON arrow",
+ "SOON",
"words"
]
},
@@ -18546,8 +18810,7 @@
"b": "1F51D",
"j": [
"arrow",
- "top",
- "TOP arrow",
+ "TOP",
"up",
"words",
"blue-square"
@@ -18689,6 +18952,15 @@
"hexagram"
]
},
+ "khanda": {
+ "a": "⊛ Khanda",
+ "b": "1FAAF",
+ "j": [
+ "khanda",
+ "religion",
+ "Sikh"
+ ]
+ },
"aries": {
"a": "Aries",
"b": "2648",
@@ -18966,7 +19238,6 @@
"j": [
"arrow",
"button",
- "red",
"blue-square",
"triangle",
"direction",
@@ -18993,7 +19264,6 @@
"arrow",
"button",
"down",
- "red",
"blue-square",
"direction",
"bottom"
@@ -19103,6 +19373,16 @@
"bars"
]
},
+ "wireless": {
+ "a": "⊛ Wireless",
+ "b": "1F6DC",
+ "j": [
+ "computer",
+ "internet",
+ "network",
+ "wireless"
+ ]
+ },
"vibration-mode": {
"a": "Vibration Mode",
"b": "1F4F3",
@@ -19213,7 +19493,7 @@
]
},
"heavy-equals-sign": {
- "a": "⊛ Heavy Equals Sign",
+ "a": "Heavy Equals Sign",
"b": "1F7F0",
"j": [
"equality",
@@ -19592,7 +19872,7 @@
"a": "Copyright",
"b": "00A9",
"j": [
- "c",
+ "C",
"ip",
"license",
"circle",
@@ -19604,7 +19884,7 @@
"a": "Registered",
"b": "00AE",
"j": [
- "r",
+ "R",
"alphabet",
"circle"
]
@@ -19614,7 +19894,7 @@
"b": "2122",
"j": [
"mark",
- "tm",
+ "TM",
"trademark",
"brand",
"law",
@@ -19812,7 +20092,7 @@
"a": "A Button (Blood Type)",
"b": "1F170",
"j": [
- "a",
+ "A",
"A button (blood type)",
"blood type",
"a_button",
@@ -19825,7 +20105,7 @@
"a": "Ab Button (Blood Type)",
"b": "1F18E",
"j": [
- "ab",
+ "AB",
"AB button (blood type)",
"blood type",
"ab_button",
@@ -19837,7 +20117,7 @@
"a": "B Button (Blood Type)",
"b": "1F171",
"j": [
- "b",
+ "B",
"B button (blood type)",
"blood type",
"b_button",
@@ -19850,7 +20130,7 @@
"a": "Cl Button",
"b": "1F191",
"j": [
- "cl",
+ "CL",
"CL button",
"alphabet",
"words",
@@ -19861,7 +20141,7 @@
"a": "Cool Button",
"b": "1F192",
"j": [
- "cool",
+ "COOL",
"COOL button",
"words",
"blue-square"
@@ -19871,7 +20151,7 @@
"a": "Free Button",
"b": "1F193",
"j": [
- "free",
+ "FREE",
"FREE button",
"blue-square",
"words"
@@ -19891,7 +20171,7 @@
"a": "Id Button",
"b": "1F194",
"j": [
- "id",
+ "ID",
"ID button",
"identity",
"purple-square",
@@ -19904,7 +20184,7 @@
"j": [
"circle",
"circled M",
- "m",
+ "M",
"alphabet",
"blue-circle",
"letter"
@@ -19914,7 +20194,7 @@
"a": "New Button",
"b": "1F195",
"j": [
- "new",
+ "NEW",
"NEW button",
"blue-square",
"words",
@@ -19925,7 +20205,7 @@
"a": "Ng Button",
"b": "1F196",
"j": [
- "ng",
+ "NG",
"NG button",
"blue-square",
"words",
@@ -19938,7 +20218,7 @@
"b": "1F17E",
"j": [
"blood type",
- "o",
+ "O",
"O button (blood type)",
"o_button",
"alphabet",
@@ -19962,6 +20242,7 @@
"a": "P Button",
"b": "1F17F",
"j": [
+ "P",
"P button",
"parking",
"cars",
@@ -19975,7 +20256,7 @@
"b": "1F198",
"j": [
"help",
- "sos",
+ "SOS",
"SOS button",
"red-square",
"words",
@@ -19988,7 +20269,8 @@
"b": "1F199",
"j": [
"mark",
- "up",
+ "UP",
+ "UP!",
"UP! button",
"blue-square",
"above",
@@ -20000,7 +20282,7 @@
"b": "1F19A",
"j": [
"versus",
- "vs",
+ "VS",
"VS button",
"words",
"orange-square"
diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index a8262fde40..dacd1416fd 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -32,11 +32,11 @@ knit {
// Note: 2 digits max for each value
ext.versionMajor = 1
-ext.versionMinor = 4
+ext.versionMinor = 5
// Note: even values are reserved for regular release, odd values for hotfix release.
// When creating a hotfix, you should decrease the value, since the current value
// is the value for the next regular release.
-ext.versionPatch = 36
+ext.versionPatch = 0
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@@ -291,6 +291,12 @@ android {
}
}
+ sourceSets {
+ nightly {
+ java.srcDirs += "src/release/java"
+ }
+ }
+
flavorDimensions "store"
productFlavors {
@@ -340,16 +346,48 @@ android {
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
]
}
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
implementation project(':vector')
implementation project(':vector-config')
+ debugImplementation project(':library:ui-styles')
implementation libs.dagger.hilt
implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.sharetarget:sharetarget:1.1.0"
+ // Flipper, debug builds only
+ debugImplementation(libs.flipper.flipper) {
+ exclude group: 'com.facebook.fbjni', module: 'fbjni'
+ }
+ debugImplementation(libs.flipper.flipperNetworkPlugin) {
+ exclude group: 'com.facebook.fbjni', module: 'fbjni'
+ }
+ debugImplementation 'com.facebook.soloader:soloader:0.10.4'
+ debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
+
+ gplayImplementation "com.google.android.gms:play-services-location:20.0.0"
+ // UnifiedPush gplay flavor only
+ gplayImplementation('com.google.firebase:firebase-messaging:23.0.8') {
+ exclude group: 'com.google.firebase', module: 'firebase-core'
+ exclude group: 'com.google.firebase', module: 'firebase-analytics'
+ exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
+ }
+
+ // Nightly
+ // API-only library
+ gplayImplementation libs.google.appdistributionApi
+ // Full SDK implementation
+ gplayImplementation libs.google.appdistribution
+
+ // OSS License, gplay flavor only
+ gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
kapt libs.dagger.hiltCompiler
+ kapt libs.airbnb.epoxyProcessor
androidTestImplementation libs.androidx.testCore
androidTestImplementation libs.androidx.testRunner
@@ -374,5 +412,6 @@ dependencies {
androidTestImplementation libs.androidx.fragmentTesting
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.7.10"
debugImplementation libs.androidx.fragmentTesting
+ debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
}
diff --git a/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
index da13e49e84..901ef8e4c1 100644
--- a/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
+++ b/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt
@@ -225,8 +225,8 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
// Wait until local secrets are known (gossip)
withIdlingResource(allSecretsKnownIdling(uiSession)) {
- onView(withId(R.id.groupToolbarAvatarImageView))
- .perform(click())
+ onView(withId(R.id.roomListContainer))
+ .check(matches(isDisplayed()))
}
}
diff --git a/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt b/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
index b70fcfec25..d9dfb0facf 100644
--- a/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
+++ b/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
@@ -50,7 +50,7 @@ import im.vector.app.withIdlingResource
import timber.log.Timber
class ElementRobot(
- private val labsPreferences: LabFeaturesPreferences = LabFeaturesPreferences(false)
+ private val labsPreferences: LabFeaturesPreferences = LabFeaturesPreferences(true)
) {
fun onboarding(block: OnboardingRobot.() -> Unit) {
block(OnboardingRobot())
@@ -110,9 +110,6 @@ class ElementRobot(
closeSoftKeyboard()
block(NewDirectMessageRobot())
pressBack()
- if (labsPreferences.isNewAppLayoutEnabled) {
- pressBack() // close create dialog
- }
waitUntilViewVisible(withId(R.id.roomListContainer))
}
@@ -121,9 +118,6 @@ class ElementRobot(
clickOn(R.id.bottom_action_rooms)
}
RoomListRobot(labsPreferences).newRoom { block() }
- if (labsPreferences.isNewAppLayoutEnabled) {
- pressBack() // close create dialog
- }
waitUntilViewVisible(withId(R.id.roomListContainer))
}
diff --git a/vector-app/src/debug/AndroidManifest.xml b/vector-app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000000..a7867f4081
--- /dev/null
+++ b/vector-app/src/debug/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
similarity index 99%
rename from vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
index eaaf021989..005e9c499b 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
@@ -34,13 +34,13 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.toast
-import im.vector.app.databinding.ActivityDebugMenuBinding
import im.vector.app.features.debug.analytics.DebugAnalyticsActivity
import im.vector.app.features.debug.features.DebugFeaturesSettingsActivity
import im.vector.app.features.debug.leak.DebugMemoryLeaksActivity
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.debug.settings.DebugPrivateSettingsActivity
import im.vector.app.features.qrcode.QrCodeScannerActivity
+import im.vector.application.databinding.ActivityDebugMenuBinding
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkDefaultActivity
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkTestActivity
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkVectorActivity
diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
index 0f00f2daa5..a9be5512e4 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
@@ -23,13 +23,13 @@ import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog
import im.vector.app.core.utils.onPermissionDeniedSnackbar
import im.vector.app.core.utils.registerForPermissionsResult
-import im.vector.app.databinding.ActivityDebugPermissionBinding
+import im.vector.application.R
+import im.vector.application.databinding.ActivityDebugPermissionBinding
import timber.log.Timber
@AndroidEntryPoint
diff --git a/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
index 59c60e0e15..6e94bce00a 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/TestLinkifyActivity.kt
@@ -20,9 +20,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
-import im.vector.app.R
-import im.vector.app.databinding.ActivityTestLinkifyBinding
-import im.vector.app.databinding.ItemTestLinkifyBinding
+import im.vector.application.R
+import im.vector.application.databinding.ActivityTestLinkifyBinding
+import im.vector.application.databinding.ItemTestLinkifyBinding
class TestLinkifyActivity : AppCompatActivity() {
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsActivity.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
index eb23fe6383..0fa11d7220 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsFragment.kt
@@ -25,7 +25,7 @@ import com.airbnb.mvrx.withState
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.toOnOff
import im.vector.app.core.platform.VectorBaseFragment
-import im.vector.app.databinding.FragmentDebugAnalyticsBinding
+import im.vector.application.databinding.FragmentDebugAnalyticsBinding
import me.gujun.android.span.span
class DebugAnalyticsFragment : VectorBaseFragment() {
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewActions.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewModel.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt b/vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/analytics/DebugAnalyticsViewState.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/di/DebugModule.kt b/vector-app/src/debug/java/im/vector/app/features/debug/di/DebugModule.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/di/DebugModule.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/di/DebugModule.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt b/vector-app/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/di/FeaturesModule.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt b/vector-app/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/di/MavericksViewModelDebugModule.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt
similarity index 94%
rename from vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt
index 1e9b88c048..38765bfa9b 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt
@@ -23,9 +23,9 @@ import android.widget.Spinner
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
-import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.application.R
@EpoxyModelClass
abstract class BooleanFeatureItem : VectorEpoxyModel(R.layout.item_feature) {
@@ -70,8 +70,8 @@ abstract class BooleanFeatureItem : VectorEpoxyModel(
}
class Holder : VectorEpoxyHolder() {
- val label by bind(im.vector.app.R.id.feature_label)
- val optionsSpinner by bind(im.vector.app.R.id.feature_options)
+ val label by bind(R.id.feature_label)
+ val optionsSpinner by bind(R.id.feature_options)
}
interface Listener {
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
similarity index 95%
rename from vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 9b2711a8c3..9118dea1e3 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -80,11 +80,6 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.forceUsageOfOpusEncoder,
factory = VectorFeatures::forceUsageOfOpusEncoder
),
- createBooleanFeature(
- label = "Start DM on first message",
- key = DebugFeatureKeys.startDmOnFirstMsg,
- factory = VectorFeatures::shouldStartDmOnFirstMessage
- ),
createBooleanFeature(
label = "Enable New App Layout",
key = DebugFeatureKeys.newAppLayoutEnabled,
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index bb4cae3201..c01c058fc6 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -73,9 +73,6 @@ class DebugVectorFeatures(
override fun forceUsageOfOpusEncoder(): Boolean = read(DebugFeatureKeys.forceUsageOfOpusEncoder)
?: vectorFeatures.forceUsageOfOpusEncoder()
- override fun shouldStartDmOnFirstMessage(): Boolean = read(DebugFeatureKeys.startDmOnFirstMsg)
- ?: vectorFeatures.shouldStartDmOnFirstMessage()
-
override fun isNewAppLayoutFeatureEnabled(): Boolean = read(DebugFeatureKeys.newAppLayoutEnabled)
?: vectorFeatures.isNewAppLayoutFeatureEnabled()
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt
similarity index 92%
rename from vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt
index 5e16182f3c..57138b9a47 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorOverrides.kt
@@ -66,13 +66,13 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides {
suspend fun setHomeserverCapabilities(block: HomeserverCapabilitiesOverride.() -> HomeserverCapabilitiesOverride) {
val capabilitiesOverride = block(forceHomeserverCapabilities.firstOrNull() ?: HomeserverCapabilitiesOverride(null, null))
context.dataStore.edit { settings ->
- when (capabilitiesOverride.canChangeDisplayName) {
+ when (val canChangeDisplayName = capabilitiesOverride.canChangeDisplayName) {
null -> settings.remove(forceCanChangeDisplayName)
- else -> settings[forceCanChangeDisplayName] = capabilitiesOverride.canChangeDisplayName
+ else -> settings[forceCanChangeDisplayName] = canChangeDisplayName
}
- when (capabilitiesOverride.canChangeAvatar) {
+ when (val canChangeAvatar = capabilitiesOverride.canChangeAvatar) {
null -> settings.remove(forceCanChangeAvatar)
- else -> settings[forceCanChangeAvatar] = capabilitiesOverride.canChangeAvatar
+ else -> settings[forceCanChangeAvatar] = canChangeAvatar
}
}
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
similarity index 94%
rename from vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
index 5231e591da..00f74515cc 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt
@@ -23,9 +23,9 @@ import android.widget.Spinner
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
-import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.application.R
@EpoxyModelClass
abstract class EnumFeatureItem : VectorEpoxyModel(R.layout.item_feature) {
@@ -70,8 +70,8 @@ abstract class EnumFeatureItem : VectorEpoxyModel(R.layo
}
class Holder : VectorEpoxyHolder() {
- val label by bind(im.vector.app.R.id.feature_label)
- val optionsSpinner by bind(im.vector.app.R.id.feature_options)
+ val label by bind(R.id.feature_label)
+ val optionsSpinner by bind(R.id.feature_options)
}
interface Listener {
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksActivity.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksActivity.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt
similarity index 96%
rename from vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt
index 2abf6487e2..e9afa9aea9 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksFragment.kt
@@ -25,7 +25,7 @@ import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.platform.VectorBaseFragment
-import im.vector.app.databinding.FragmentDebugMemoryLeaksBinding
+import im.vector.application.databinding.FragmentDebugMemoryLeaksBinding
@AndroidEntryPoint
class DebugMemoryLeaksFragment :
diff --git a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewActions.kt b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewActions.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewActions.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewActions.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt
similarity index 98%
rename from vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt
index 5432cb0888..26eb1c1025 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 New Vector Ltd
+ * Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewState.kt b/vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewState.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewState.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/leak/DebugMemoryLeaksViewState.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/sas/DebugSasEmojiActivity.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiController.kt b/vector-app/src/debug/java/im/vector/app/features/debug/sas/SasEmojiController.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiController.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/sas/SasEmojiController.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt b/vector-app/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt
similarity index 98%
rename from vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt
index 179ee35693..bbc438e4b2 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt
@@ -21,9 +21,9 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
-import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.application.R
import me.gujun.android.span.image
import me.gujun.android.span.span
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsActivity.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt
index be3d41e0e1..020c228521 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsFragment.kt
@@ -25,8 +25,8 @@ import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.core.platform.VectorBaseFragment
-import im.vector.app.databinding.FragmentDebugPrivateSettingsBinding
import im.vector.app.features.home.room.list.home.release.ReleaseNotesActivity
+import im.vector.application.databinding.FragmentDebugPrivateSettingsBinding
class DebugPrivateSettingsFragment : VectorBaseFragment() {
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewActions.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewActions.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewActions.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewActions.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewModel.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewModel.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewModel.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewModel.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewState.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewState.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewState.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/DebugPrivateSettingsViewState.kt
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt
similarity index 97%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt
index 7f510ee5e9..2800b7bd8d 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/settings/OverrideDropdownView.kt
@@ -24,7 +24,7 @@ import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.LinearLayout
-import im.vector.app.databinding.ViewBooleanDropdownBinding
+import im.vector.application.databinding.ViewBooleanDropdownBinding
class OverrideDropdownView @JvmOverloads constructor(
context: Context,
diff --git a/vector/src/debug/java/im/vector/app/features/debug/settings/PrivateSettingOverrides.kt b/vector-app/src/debug/java/im/vector/app/features/debug/settings/PrivateSettingOverrides.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/features/debug/settings/PrivateSettingOverrides.kt
rename to vector-app/src/debug/java/im/vector/app/features/debug/settings/PrivateSettingOverrides.kt
diff --git a/vector/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt b/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
rename to vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
diff --git a/vector/src/debug/java/im/vector/app/leakcanary/LeakCanaryLeakDetector.kt b/vector-app/src/debug/java/im/vector/app/leakcanary/LeakCanaryLeakDetector.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/leakcanary/LeakCanaryLeakDetector.kt
rename to vector-app/src/debug/java/im/vector/app/leakcanary/LeakCanaryLeakDetector.kt
diff --git a/vector/src/debug/java/im/vector/app/receivers/VectorDebugReceiver.kt b/vector-app/src/debug/java/im/vector/app/receivers/VectorDebugReceiver.kt
similarity index 100%
rename from vector/src/debug/java/im/vector/app/receivers/VectorDebugReceiver.kt
rename to vector-app/src/debug/java/im/vector/app/receivers/VectorDebugReceiver.kt
diff --git a/vector/src/debug/res/layout/activity_debug_menu.xml b/vector-app/src/debug/res/layout/activity_debug_menu.xml
similarity index 100%
rename from vector/src/debug/res/layout/activity_debug_menu.xml
rename to vector-app/src/debug/res/layout/activity_debug_menu.xml
diff --git a/vector/src/debug/res/layout/activity_debug_permission.xml b/vector-app/src/debug/res/layout/activity_debug_permission.xml
similarity index 100%
rename from vector/src/debug/res/layout/activity_debug_permission.xml
rename to vector-app/src/debug/res/layout/activity_debug_permission.xml
diff --git a/vector/src/debug/res/layout/activity_test_linkify.xml b/vector-app/src/debug/res/layout/activity_test_linkify.xml
similarity index 100%
rename from vector/src/debug/res/layout/activity_test_linkify.xml
rename to vector-app/src/debug/res/layout/activity_test_linkify.xml
diff --git a/vector/src/debug/res/layout/demo_theme_sample.xml b/vector-app/src/debug/res/layout/demo_theme_sample.xml
similarity index 100%
rename from vector/src/debug/res/layout/demo_theme_sample.xml
rename to vector-app/src/debug/res/layout/demo_theme_sample.xml
diff --git a/vector/src/debug/res/layout/demo_themes.xml b/vector-app/src/debug/res/layout/demo_themes.xml
similarity index 100%
rename from vector/src/debug/res/layout/demo_themes.xml
rename to vector-app/src/debug/res/layout/demo_themes.xml
diff --git a/vector/src/debug/res/layout/fragment_debug_analytics.xml b/vector-app/src/debug/res/layout/fragment_debug_analytics.xml
similarity index 100%
rename from vector/src/debug/res/layout/fragment_debug_analytics.xml
rename to vector-app/src/debug/res/layout/fragment_debug_analytics.xml
diff --git a/vector/src/debug/res/layout/fragment_debug_memory_leaks.xml b/vector-app/src/debug/res/layout/fragment_debug_memory_leaks.xml
similarity index 100%
rename from vector/src/debug/res/layout/fragment_debug_memory_leaks.xml
rename to vector-app/src/debug/res/layout/fragment_debug_memory_leaks.xml
diff --git a/vector/src/debug/res/layout/fragment_debug_private_settings.xml b/vector-app/src/debug/res/layout/fragment_debug_private_settings.xml
similarity index 100%
rename from vector/src/debug/res/layout/fragment_debug_private_settings.xml
rename to vector-app/src/debug/res/layout/fragment_debug_private_settings.xml
diff --git a/vector/src/debug/res/layout/item_feature.xml b/vector-app/src/debug/res/layout/item_feature.xml
similarity index 100%
rename from vector/src/debug/res/layout/item_feature.xml
rename to vector-app/src/debug/res/layout/item_feature.xml
diff --git a/vector/src/debug/res/layout/item_sas_emoji.xml b/vector-app/src/debug/res/layout/item_sas_emoji.xml
similarity index 100%
rename from vector/src/debug/res/layout/item_sas_emoji.xml
rename to vector-app/src/debug/res/layout/item_sas_emoji.xml
diff --git a/vector/src/debug/res/layout/item_test_linkify.xml b/vector-app/src/debug/res/layout/item_test_linkify.xml
similarity index 100%
rename from vector/src/debug/res/layout/item_test_linkify.xml
rename to vector-app/src/debug/res/layout/item_test_linkify.xml
diff --git a/vector/src/debug/res/layout/view_boolean_dropdown.xml b/vector-app/src/debug/res/layout/view_boolean_dropdown.xml
similarity index 100%
rename from vector/src/debug/res/layout/view_boolean_dropdown.xml
rename to vector-app/src/debug/res/layout/view_boolean_dropdown.xml
diff --git a/vector/src/debug/res/values/strings.xml b/vector-app/src/debug/res/values/strings.xml
similarity index 100%
rename from vector/src/debug/res/values/strings.xml
rename to vector-app/src/debug/res/values/strings.xml
diff --git a/vector/src/debug/res/xml/shortcuts.xml b/vector-app/src/debug/res/xml/shortcuts.xml
similarity index 100%
rename from vector/src/debug/res/xml/shortcuts.xml
rename to vector-app/src/debug/res/xml/shortcuts.xml
diff --git a/vector/src/fdroid/AndroidManifest.xml b/vector-app/src/fdroid/AndroidManifest.xml
similarity index 78%
rename from vector/src/fdroid/AndroidManifest.xml
rename to vector-app/src/fdroid/AndroidManifest.xml
index 15db89ca13..354d450958 100644
--- a/vector/src/fdroid/AndroidManifest.xml
+++ b/vector-app/src/fdroid/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
@@ -15,7 +14,7 @@
@@ -24,12 +23,12 @@
diff --git a/vector/src/fdroid/java/im/vector/app/di/FlavorModule.kt b/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/di/FlavorModule.kt
rename to vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt
diff --git a/vector/src/fdroid/java/im/vector/app/di/NotificationTestModule.kt b/vector-app/src/fdroid/java/im/vector/app/di/NotificationTestModule.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/di/NotificationTestModule.kt
rename to vector-app/src/fdroid/java/im/vector/app/di/NotificationTestModule.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/package-info.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/package-info.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/package-info.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/package-info.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
similarity index 95%
rename from vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
index bd1e0eb0ee..bccbf42e92 100644
--- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
+++ b/vector-app/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt
@@ -16,6 +16,7 @@
package im.vector.app.fdroid.receiver
+import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
@@ -65,6 +66,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
companion object {
private const val REQUEST_CODE = 0
+ @SuppressLint("WrongConstant") // PendingIntentCompat.FLAG_IMMUTABLE is a false positive
fun scheduleAlarm(context: Context, sessionId: String, delayInSeconds: Int, clock: Clock) {
// Reschedule
Timber.v("## Sync: Scheduling alarm for background sync in $delayInSeconds seconds")
@@ -87,6 +89,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
}
}
+ @SuppressLint("WrongConstant") // PendingIntentCompat.FLAG_IMMUTABLE is a false positive
fun cancelAlarm(context: Context) {
Timber.v("## Sync: Cancel alarm for background sync")
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/service/FDroidGuardServiceStarter.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/service/FDroidGuardServiceStarter.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/service/FDroidGuardServiceStarter.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/service/FDroidGuardServiceStarter.kt
diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/service/GuardAndroidService.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/service/GuardAndroidService.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/fdroid/service/GuardAndroidService.kt
rename to vector-app/src/fdroid/java/im/vector/app/fdroid/service/GuardAndroidService.kt
diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt
rename to vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt
diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/FdroidNotificationTroubleshootTestManagerFactory.kt b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidNotificationTroubleshootTestManagerFactory.kt
similarity index 100%
rename from vector/src/fdroid/java/im/vector/app/push/fcm/FdroidNotificationTroubleshootTestManagerFactory.kt
rename to vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidNotificationTroubleshootTestManagerFactory.kt
diff --git a/vector/src/gplay/java/im/vector/app/GoogleFlavorLegals.kt b/vector-app/src/gplay/java/im/vector/app/GoogleFlavorLegals.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/GoogleFlavorLegals.kt
rename to vector-app/src/gplay/java/im/vector/app/GoogleFlavorLegals.kt
diff --git a/vector/src/gplay/java/im/vector/app/di/FlavorModule.kt b/vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/di/FlavorModule.kt
rename to vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt
diff --git a/vector/src/gplay/java/im/vector/app/di/NotificationTestModule.kt b/vector-app/src/gplay/java/im/vector/app/di/NotificationTestModule.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/di/NotificationTestModule.kt
rename to vector-app/src/gplay/java/im/vector/app/di/NotificationTestModule.kt
diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
rename to vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
rename to vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
rename to vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
diff --git a/vector/src/gplay/java/im/vector/app/gplay/package-info.kt b/vector-app/src/gplay/java/im/vector/app/gplay/package-info.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/gplay/package-info.kt
rename to vector-app/src/gplay/java/im/vector/app/gplay/package-info.kt
diff --git a/vector/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt b/vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt
rename to vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt
diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt
rename to vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt
diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/GoogleNotificationTroubleshootTestManagerFactory.kt b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleNotificationTroubleshootTestManagerFactory.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/push/fcm/GoogleNotificationTroubleshootTestManagerFactory.kt
rename to vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleNotificationTroubleshootTestManagerFactory.kt
diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/VectorFirebaseMessagingService.kt b/vector-app/src/gplay/java/im/vector/app/push/fcm/VectorFirebaseMessagingService.kt
similarity index 100%
rename from vector/src/gplay/java/im/vector/app/push/fcm/VectorFirebaseMessagingService.kt
rename to vector-app/src/gplay/java/im/vector/app/push/fcm/VectorFirebaseMessagingService.kt
diff --git a/vector-app/src/main/AndroidManifest.xml b/vector-app/src/main/AndroidManifest.xml
index 84607cf3d7..bff594c0de 100644
--- a/vector-app/src/main/AndroidManifest.xml
+++ b/vector-app/src/main/AndroidManifest.xml
@@ -18,6 +18,24 @@
tools:ignore="UnusedAttribute"
tools:replace="android:allowBackup">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/nightly/res/xml/shortcuts.xml b/vector-app/src/nightly/res/xml/shortcuts.xml
similarity index 100%
rename from vector/src/nightly/res/xml/shortcuts.xml
rename to vector-app/src/nightly/res/xml/shortcuts.xml
diff --git a/vector/src/release/java/im/vector/app/core/di/DebugModule.kt b/vector-app/src/release/java/im/vector/app/core/di/DebugModule.kt
similarity index 100%
rename from vector/src/release/java/im/vector/app/core/di/DebugModule.kt
rename to vector-app/src/release/java/im/vector/app/core/di/DebugModule.kt
diff --git a/vector/src/release/java/im/vector/app/core/di/FeaturesModule.kt b/vector-app/src/release/java/im/vector/app/core/di/FeaturesModule.kt
similarity index 100%
rename from vector/src/release/java/im/vector/app/core/di/FeaturesModule.kt
rename to vector-app/src/release/java/im/vector/app/core/di/FeaturesModule.kt
diff --git a/vector/src/release/java/im/vector/app/receivers/DebugReceiver.kt b/vector-app/src/release/java/im/vector/app/receivers/DebugReceiver.kt
similarity index 100%
rename from vector/src/release/java/im/vector/app/receivers/DebugReceiver.kt
rename to vector-app/src/release/java/im/vector/app/receivers/DebugReceiver.kt
diff --git a/vector/src/release/res/xml/shortcuts.xml b/vector-app/src/release/res/xml/shortcuts.xml
similarity index 100%
rename from vector/src/release/res/xml/shortcuts.xml
rename to vector-app/src/release/res/xml/shortcuts.xml
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index 1701fd45b0..c69452e3d0 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -37,8 +37,10 @@
true
+ true
+ truefalse
- false
+ truetruefalse
diff --git a/vector/build.gradle b/vector/build.gradle
index a5538053fc..ac3699454c 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -66,23 +66,6 @@ android {
testCoverageEnabled = coverage.enableTestCoverage
}
}
- nightly {
- initWith release
- matchingFallbacks = ['release']
- }
- release
- }
-
- flavorDimensions "store"
-
- productFlavors {
- gplay {
- dimension "store"
- }
-
- fdroid {
- dimension "store"
- }
}
compileOptions {
@@ -111,17 +94,12 @@ android {
test {
java.srcDirs += "src/sharedTest/java"
}
- // Add sourceSets for `release` version when building `nightly`
- nightly {
- java.srcDirs += "src/release/java"
- }
}
buildFeatures {
viewBinding true
}
}
-
dependencies {
implementation project(":vector-config")
api project(":matrix-sdk-android")
@@ -183,12 +161,6 @@ dependencies {
// Snap Helper https://github.com/rubensousa/GravitySnapHelper
api 'com.github.rubensousa:gravitysnaphelper:2.2.2'
- // Nightly
- // API-only library
- gplayImplementation libs.google.appdistributionApi
- // Full SDK implementation
- gplayImplementation libs.google.appdistribution
-
// Work
api libs.androidx.work
@@ -204,7 +176,9 @@ dependencies {
// UI
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
implementation libs.google.material
- implementation 'me.gujun.android:span:1.7'
+ api('me.gujun.android:span:1.7') {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ }
implementation libs.markwon.core
implementation libs.markwon.extLatex
implementation libs.markwon.inlineParser
@@ -233,7 +207,6 @@ dependencies {
// Image Loading
implementation libs.github.bigImageViewer
implementation libs.github.glideImageLoader
- implementation libs.github.progressPieIndicator
implementation libs.github.glideImageViewFactory
// implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2'
@@ -252,19 +225,12 @@ dependencies {
kapt libs.dagger.hiltCompiler
// Analytics
- implementation 'com.posthog.android:posthog:1.1.2'
+ implementation('com.posthog.android:posthog:1.1.2') {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ }
// UnifiedPush
implementation 'com.github.UnifiedPush:android-connector:2.0.1'
- // UnifiedPush gplay flavor only
- gplayImplementation('com.google.firebase:firebase-messaging:23.0.8') {
- exclude group: 'com.google.firebase', module: 'firebase-core'
- exclude group: 'com.google.firebase', module: 'firebase-analytics'
- exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
- }
-
- // OSS License, gplay flavor only
- gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
implementation "androidx.emoji2:emoji2:1.1.0"
@@ -278,12 +244,22 @@ dependencies {
exclude group: 'com.google.firebase'
exclude group: 'com.google.android.gms'
exclude group: 'com.android.installreferrer'
+
+ // Exclude jitsi's android-scalablevideoview fork's support library
+ // The library exports a jetified artifact but doesn't remove the support library dependency
+ // https://github.com/MatrixFrog/Android-ScalableVideoView/blob/master/gradle.properties#L1
+ exclude group: 'com.android.support', module: 'appcompat-v7'
}
// QR-code
// Stick to 3.3.3 because of https://github.com/zxing/zxing/issues/1170
implementation 'com.google.zxing:core:3.3.3'
- implementation 'me.dm7.barcodescanner:zxing:1.9.13'
+
+ // Excludes the legacy support library annotation usages
+ // https://github.com/dm77/barcodescanner/blob/d036996c8a6f36a68843ffe539c834c28944b2d5/core/src/main/java/me/dm7/barcodescanner/core/CameraWrapper.java#L4
+ implementation ('me.dm7.barcodescanner:zxing:1.9.13') {
+ exclude group: 'com.android.support', module: 'support-v4'
+ }
// Emoji Keyboard
api libs.vanniktech.emojiMaterial
@@ -300,14 +276,12 @@ dependencies {
implementation 'commons-codec:commons-codec:1.15'
// MapTiler
- fdroidApi(libs.maplibre.androidSdk) {
+ api(libs.maplibre.androidSdk) {
exclude group: 'com.google.android.gms', module: 'play-services-location'
}
- fdroidApi(libs.maplibre.pluginAnnotation) {
+ api(libs.maplibre.pluginAnnotation) {
exclude group: 'com.google.android.gms', module: 'play-services-location'
}
- gplayApi libs.maplibre.androidSdk
- gplayApi libs.maplibre.pluginAnnotation
// TESTS
testImplementation libs.tests.junit
@@ -320,19 +294,6 @@ dependencies {
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
}
- // Flipper, debug builds only
- debugImplementation(libs.flipper.flipper) {
- exclude group: 'com.facebook.fbjni', module: 'fbjni'
- }
- debugImplementation(libs.flipper.flipperNetworkPlugin) {
- exclude group: 'com.facebook.fbjni', module: 'fbjni'
- }
- debugImplementation 'com.facebook.soloader:soloader:0.10.4'
- debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
-
- // Activate when you want to check for leaks, from time to time.
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
-
androidTestImplementation libs.androidx.testCore
androidTestImplementation libs.androidx.testRunner
androidTestImplementation libs.androidx.testRules
diff --git a/vector/src/androidTest/AndroidManifest.xml b/vector/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000000..5c3b99d4d1
--- /dev/null
+++ b/vector/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/vector/src/debug/AndroidManifest.xml b/vector/src/debug/AndroidManifest.xml
deleted file mode 100644
index 94fdb1b389..0000000000
--- a/vector/src/debug/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index c55852d7d8..ecc721524d 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -90,23 +90,6 @@
android:name=".features.MainActivity"
android:theme="@style/Theme.Vector.Launcher" />
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
index 8bcfd4e422..6fb2505386 100644
--- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
@@ -88,6 +88,8 @@ import im.vector.app.features.settings.account.deactivation.DeactivateAccountVie
import im.vector.app.features.settings.crosssigning.CrossSigningSettingsViewModel
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheetViewModel
import im.vector.app.features.settings.devices.DevicesViewModel
+import im.vector.app.features.settings.devices.v2.details.SessionDetailsViewModel
+import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsViewModel
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewViewModel
import im.vector.app.features.settings.devtools.AccountDataViewModel
import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailViewModel
@@ -641,4 +643,14 @@ interface MavericksViewModelModule {
@IntoMap
@MavericksViewModelKey(SessionOverviewViewModel::class)
fun sessionOverviewViewModelFactory(factory: SessionOverviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
+ @Binds
+ @IntoMap
+ @MavericksViewModelKey(OtherSessionsViewModel::class)
+ fun otherSessionsViewModelFactory(factory: OtherSessionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
+ @Binds
+ @IntoMap
+ @MavericksViewModelKey(SessionDetailsViewModel::class)
+ fun sessionDetailsViewModelFactory(factory: SessionDetailsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
}
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
index ddc281fdd1..ec6f3288f8 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
@@ -25,6 +25,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.CallSuper
+import androidx.annotation.FloatRange
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
@@ -39,6 +40,7 @@ import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.MobileScreen
+import io.github.hyuwah.draggableviewlib.Utils
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
@@ -165,6 +167,13 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe
forceExpandState()
}
+ protected fun setPeekHeightAsScreenPercentage(@FloatRange(from = 0.0, to = 1.0) percentage: Float) {
+ context?.let {
+ val screenHeight = Utils.getScreenHeight(it)
+ bottomSheetBehavior?.setPeekHeight((screenHeight * percentage).toInt(), true)
+ }
+ }
+
protected fun forceExpandState() {
if (showExpanded) {
// Force the bottom sheet to be expanded
diff --git a/vector/src/main/java/im/vector/app/core/utils/CopyToClipboardUseCase.kt b/vector/src/main/java/im/vector/app/core/utils/CopyToClipboardUseCase.kt
new file mode 100644
index 0000000000..19ad9e2bba
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/utils/CopyToClipboardUseCase.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.core.utils
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import androidx.core.content.getSystemService
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+
+class CopyToClipboardUseCase @Inject constructor(
+ @ApplicationContext private val context: Context,
+) {
+
+ fun execute(text: CharSequence) {
+ context.getSystemService()
+ ?.setPrimaryClip(ClipData.newPlainText("", text))
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/core/utils/FirstItemUpdatedObserver.kt b/vector/src/main/java/im/vector/app/core/utils/FirstItemUpdatedObserver.kt
new file mode 100644
index 0000000000..25901cdf95
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/utils/FirstItemUpdatedObserver.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.core.utils
+
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+/**
+ * This observer detects when item was added or moved to the first position of the adapter, while recyclerView is scrolled to the top. This is necessary
+ * to force recycler to scroll to the top to make such item visible, because by default it will keep items on screen, while adding new item to the top,
+ * outside of the viewport
+ * @param layoutManager - [LinearLayoutManager] of the recycler view, which displays items
+ * @property onItemUpdated - callback to be called, when observer detects event
+ */
+class FirstItemUpdatedObserver(
+ layoutManager: LinearLayoutManager,
+ private val onItemUpdated: () -> Unit
+) : RecyclerView.AdapterDataObserver() {
+
+ val layoutManager: LinearLayoutManager? by weak(layoutManager)
+
+ override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
+ if ((toPosition == 0 || fromPosition == 0) && layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) {
+ onItemUpdated.invoke()
+ }
+ }
+
+ override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+ if (positionStart == 0 && layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) {
+ onItemUpdated.invoke()
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
index 6cfe8acc16..cde4fe2a35 100644
--- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
@@ -19,8 +19,6 @@ package im.vector.app.core.utils
import android.annotation.TargetApi
import android.app.Activity
import android.content.ActivityNotFoundException
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -100,8 +98,7 @@ fun requestDisablingBatteryOptimization(activity: Activity, activityResultLaunch
* @param toastMessage content of the toast message as a String resource
*/
fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = true, @StringRes toastMessage: Int = R.string.copied_to_clipboard) {
- val clipboard = context.getSystemService()!!
- clipboard.setPrimaryClip(ClipData.newPlainText("", text))
+ CopyToClipboardUseCase(context).execute(text)
if (showToast) {
context.toast(toastMessage)
}
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index dbdb0ba1c7..e1c083db29 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -33,7 +33,6 @@ interface VectorFeatures {
fun isScreenSharingEnabled(): Boolean
fun isLocationSharingEnabled(): Boolean
fun forceUsageOfOpusEncoder(): Boolean
- fun shouldStartDmOnFirstMessage(): Boolean
/**
* This is only to enable if the labs flag should be visible and effective.
@@ -56,7 +55,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isScreenSharingEnabled(): Boolean = true
override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING
override fun forceUsageOfOpusEncoder(): Boolean = false
- override fun shouldStartDmOnFirstMessage(): Boolean = false
override fun isNewAppLayoutFeatureEnabled(): Boolean = true
override fun isNewDeviceManagementEnabled(): Boolean = false
}
diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
index 36ee47ca06..3f67708a28 100644
--- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt
@@ -26,29 +26,28 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel
-import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
+import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.userdirectory.PendingSelection
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
-import org.matrix.android.sdk.api.session.user.model.User
class CreateDirectRoomViewModel @AssistedInject constructor(
@Assisted initialState: CreateDirectRoomViewState,
private val rawService: RawService,
+ private val vectorPreferences: VectorPreferences,
val session: Session,
val analyticsTracker: AnalyticsTracker,
- val vectorFeatures: VectorFeatures
) :
VectorViewModel(initialState) {
@@ -78,11 +77,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(
_viewEvents.post(CreateDirectRoomViewEvents.DmSelf)
} else {
// Try to get user from known users and fall back to creating a User object from MXID
- val qrInvitee = if (session.getUser(mxid) != null) {
- session.getUser(mxid)!!
- } else {
- User(mxid, null, null)
- }
+ val qrInvitee = session.getUserOrDefault(mxid)
onSubmitInvitees(setOf(PendingSelection.UserPendingSelection(qrInvitee)))
}
}
@@ -129,7 +124,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(
}
val result = runCatchingToAsync {
- if (vectorFeatures.shouldStartDmOnFirstMessage()) {
+ if (vectorPreferences.isDeferredDmEnabled()) {
session.roomService().createLocalRoom(roomParams)
} else {
analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse()))
diff --git a/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt b/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt
index c2cc13920f..466aca1176 100644
--- a/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt
@@ -16,11 +16,11 @@
package im.vector.app.features.createdirect
-import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
+import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService
@@ -32,7 +32,7 @@ class DirectRoomHelper @Inject constructor(
private val rawService: RawService,
private val session: Session,
private val analyticsTracker: AnalyticsTracker,
- private val vectorFeatures: VectorFeatures,
+ private val vectorPreferences: VectorPreferences,
) {
suspend fun ensureDMExists(userId: String): String {
@@ -50,7 +50,7 @@ class DirectRoomHelper @Inject constructor(
setDirectMessage()
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
}
- roomId = if (vectorFeatures.shouldStartDmOnFirstMessage()) {
+ roomId = if (vectorPreferences.isDeferredDmEnabled()) {
session.roomService().createLocalRoom(roomParams)
} else {
analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse()))
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 91bb3fa7f2..3406a86d1e 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -31,6 +31,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificatio
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
import javax.inject.Inject
@@ -67,8 +68,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
when (tx.state) {
is VerificationTxState.OnStarted -> {
// Add a notification for every incoming request
- val user = session?.userService()?.getUser(tx.otherUserId)
- val name = user?.toMatrixItem()?.getBestName() ?: tx.otherUserId
+ val user = session.getUserOrDefault(tx.otherUserId).toMatrixItem()
+ val name = user.getBestName()
val alert = VerificationVectorAlert(
uid,
context.getString(R.string.sas_incoming_request_notif_title),
@@ -86,7 +87,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
)
.apply {
- viewBinder = VerificationVectorAlert.ViewBinder(user?.toMatrixItem(), avatarRenderer.get())
+ viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer.get())
contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId)
@@ -131,8 +132,8 @@ class IncomingVerificationRequestHandler @Inject constructor(
// XXX this is a bit hard coded :/
popupAlertManager.cancelAlert("review_login")
}
- val user = session?.userService()?.getUser(pr.otherUserId)?.toMatrixItem()
- val name = user?.getBestName() ?: pr.otherUserId
+ val user = session.getUserOrDefault(pr.otherUserId).toMatrixItem()
+ val name = user.getBestName()
val description = if (name == pr.otherUserId) {
name
} else {
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt
index eae868eb26..38b72f2022 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt
@@ -152,29 +152,25 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment
- state.otherUserMxItem?.let { matrixItem ->
- if (state.isMe) {
- avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
- if (state.sasTransactionState == VerificationTxState.Verified ||
- state.qrTransactionState == VerificationTxState.Verified ||
- state.verifiedFromPrivateKeys) {
- views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
- } else {
- views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
- }
- views.otherUserNameText.text = getString(
- if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
- )
+ avatarRenderer.render(state.otherUserMxItem, views.otherUserAvatarImageView)
+ if (state.isMe) {
+ if (state.sasTransactionState == VerificationTxState.Verified ||
+ state.qrTransactionState == VerificationTxState.Verified ||
+ state.verifiedFromPrivateKeys) {
+ views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else {
- avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
-
- if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
- views.otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
- views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
- } else {
- views.otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName())
- views.otherUserShield.render(null)
- }
+ views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
+ }
+ views.otherUserNameText.text = getString(
+ if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
+ )
+ } else {
+ if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
+ views.otherUserNameText.text = getString(R.string.verification_verified_user, state.otherUserMxItem.getBestName())
+ views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
+ } else {
+ views.otherUserNameText.text = getString(R.string.verification_verify_user, state.otherUserMxItem.getBestName())
+ views.otherUserShield.render(null)
}
}
@@ -235,7 +231,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment = Uninitialized,
val pendingLocalId: String? = null,
val sasTransactionState: VerificationTxState? = null,
@@ -92,7 +93,8 @@ data class VerificationBottomSheetViewState(
otherUserId = args.otherUserId,
verificationId = args.verificationId,
roomId = args.roomId,
- selfVerificationMode = args.selfVerificationMode
+ selfVerificationMode = args.selfVerificationMode,
+ otherUserMxItem = MatrixItem.UserItem(args.otherUserId),
)
}
@@ -126,7 +128,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
}
- val userItem = session.getUser(initialState.otherUserId)
+ fetchOtherUserProfile(initialState.otherUserId)
var autoReady = false
val pr = if (initialState.selfVerificationMode) {
@@ -160,7 +162,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
setState {
copy(
- otherUserMxItem = userItem?.toMatrixItem(),
sasTransactionState = sasTx?.state,
qrTransactionState = qrTx?.state,
transactionId = pr?.transactionId ?: initialState.verificationId,
@@ -183,6 +184,28 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
}
+ private fun fetchOtherUserProfile(otherUserId: String) {
+ session.getUser(otherUserId)?.toMatrixItem()?.let {
+ setState {
+ copy(
+ otherUserMxItem = it
+ )
+ }
+ }
+ // Always fetch the latest User data
+ viewModelScope.launch {
+ tryOrNull { session.userService().resolveUser(otherUserId) }
+ ?.toMatrixItem()
+ ?.let {
+ setState {
+ copy(
+ otherUserMxItem = it
+ )
+ }
+ }
+ }
+ }
+
override fun onCleared() {
session.cryptoService().verificationService().removeListener(this)
super.onCleared()
@@ -216,12 +239,12 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
private fun cancelAllPendingVerifications(state: VerificationBottomSheetViewState) {
session.cryptoService()
- .verificationService().getExistingVerificationRequest(state.otherUserMxItem?.id ?: "", state.transactionId)?.let {
+ .verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)?.let {
session.cryptoService().verificationService().cancelVerificationRequest(it)
}
session.cryptoService()
.verificationService()
- .getExistingTransaction(state.otherUserMxItem?.id ?: "", state.transactionId ?: "")
+ .getExistingTransaction(state.otherUserId, state.transactionId ?: "")
?.cancel(CancelCode.User)
}
@@ -249,7 +272,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
override fun handle(action: VerificationAction) = withState { state ->
- val otherUserId = state.otherUserMxItem?.id ?: return@withState
+ val otherUserId = state.otherUserId
val roomId = state.roomId
?: session.roomService().getExistingDirectRoomWithUser(otherUserId)
@@ -514,7 +537,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
if (state.selfVerificationMode && state.transactionId == null) {
// is this an incoming with that user
- if (tx.isIncoming && tx.otherUserId == state.otherUserMxItem?.id) {
+ if (tx.isIncoming && tx.otherUserId == state.otherUserId) {
// Also auto accept incoming if needed!
if (tx is IncomingSasVerificationTransaction) {
if (tx.uxState == IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) {
@@ -564,7 +587,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
if (state.selfVerificationMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
// is this an incoming with that user
- if (pr.isIncoming && pr.otherUserId == state.otherUserMxItem?.id) {
+ if (pr.isIncoming && pr.otherUserId == state.otherUserId) {
if (!pr.isReady) {
// auto ready in this case, as we are waiting
// TODO, can I be here in DM mode? in this case should test if roomID is null?
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelController.kt
index 1adafe2760..600d5e1be1 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelController.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelController.kt
@@ -26,6 +26,7 @@ import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
+import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject
@@ -60,8 +61,8 @@ class VerificationCancelController @Inject constructor(
}
}
} else {
- val otherUserID = state.otherUserMxItem?.id ?: ""
- val otherDisplayName = state.otherUserMxItem?.displayName ?: ""
+ val otherUserID = state.otherUserId
+ val otherDisplayName = state.otherUserMxItem.getBestName()
bottomSheetVerificationNoticeItem {
id("notice")
notice(
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
index 45f7908446..9f908d83f6 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
@@ -78,7 +78,7 @@ class VerificationChooseMethodFragment :
override fun doVerifyBySas() = withState(sharedViewModel) { state ->
sharedViewModel.handle(
VerificationAction.StartSASVerification(
- state.otherUserMxItem?.id ?: "",
+ state.otherUserId,
state.pendingRequest.invoke()?.transactionId ?: ""
)
)
@@ -130,7 +130,7 @@ class VerificationChooseMethodFragment :
private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { state ->
sharedViewModel.handle(
VerificationAction.RemoteQrCodeScanned(
- state.otherUserMxItem?.id ?: "",
+ state.otherUserId,
state.pendingRequest.invoke()?.transactionId ?: "",
remoteQrCode
)
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt
index 98b163f4e3..bf514249d8 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt
@@ -136,7 +136,7 @@ class VerificationEmojiCodeController @Inject constructor(
if (state.isWaitingFromOther) {
bottomSheetVerificationWaitingItem {
id("waiting")
- title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUser?.getBestName() ?: ""))
+ title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUser.getBestName()))
}
} else {
bottomSheetVerificationActionItem {
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt
index 1e1a8d0710..58b5c01923 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt
@@ -68,13 +68,13 @@ class VerificationEmojiCodeFragment :
}
override fun onMatchButtonTapped() = withState(viewModel) { state ->
- val otherUserId = state.otherUser?.id ?: return@withState
+ val otherUserId = state.otherUser.id
val txId = state.transactionId ?: return@withState
sharedViewModel.handle(VerificationAction.SASMatchAction(otherUserId, txId))
}
override fun onDoNotMatchButtonTapped() = withState(viewModel) { state ->
- val otherUserId = state.otherUser?.id ?: return@withState
+ val otherUserId = state.otherUser.id
val txId = state.transactionId ?: return@withState
sharedViewModel.handle(VerificationAction.SASDoNotMatchAction(otherUserId, txId))
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
index db9a8fed4a..6761d98a55 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt
@@ -40,13 +40,13 @@ import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTra
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
data class VerificationEmojiCodeViewState(
val transactionId: String?,
- val otherUser: MatrixItem? = null,
+ val otherUser: MatrixItem,
val supportsEmoji: Boolean = true,
val emojiDescription: Async> = Uninitialized,
val decimalDescription: Async = Uninitialized,
@@ -59,15 +59,13 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor(
) : VectorViewModel(initialState), VerificationService.Listener {
init {
- withState { state ->
- refreshStateFromTx(
- session.cryptoService().verificationService()
- .getExistingTransaction(
- state.otherUser?.id ?: "", state.transactionId
- ?: ""
- ) as? SasVerificationTransaction
- )
- }
+ refreshStateFromTx(
+ session.cryptoService().verificationService()
+ .getExistingTransaction(
+ otherUserId = initialState.otherUser.id,
+ tid = initialState.transactionId ?: ""
+ ) as? SasVerificationTransaction
+ )
session.cryptoService().verificationService().addListener(this)
}
@@ -165,10 +163,10 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() {
- override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState? {
+ override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState {
val args = viewModelContext.args()
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
- val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem()
+ val matrixItem = session.getUserOrDefault(args.otherUserId).toMatrixItem()
return VerificationEmojiCodeViewState(
transactionId = args.verificationId,
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
index 71d64b99bc..639ebac29e 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
@@ -54,7 +54,7 @@ class VerificationQrScannedByOtherController @Inject constructor(
if (state.isMe) {
notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence())
} else {
- val name = state.otherUserMxItem?.getBestName() ?: ""
+ val name = state.otherUserMxItem.getBestName()
notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name).toEpoxyCharSequence())
}
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
index e050c5bfb0..185ee48351 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
@@ -52,7 +52,6 @@ class VerificationRequestController @Inject constructor(
override fun buildModels() {
val state = viewState ?: return
- val matrixItem = viewState?.otherUserMxItem ?: return
val host = this
if (state.selfVerificationMode) {
@@ -107,11 +106,9 @@ class VerificationRequestController @Inject constructor(
if (state.isMe) {
stringProvider.getString(R.string.verify_new_session_notice)
} else {
- matrixItem.let {
- stringProvider.getString(R.string.verification_request_notice, it.id)
- .toSpannable()
- .colorizeMatchingText(it.id, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
- }
+ stringProvider.getString(R.string.verification_request_notice, state.otherUserId)
+ .toSpannable()
+ .colorizeMatchingText(state.otherUserId, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
}
bottomSheetVerificationNoticeItem {
@@ -138,7 +135,7 @@ class VerificationRequestController @Inject constructor(
is Loading -> {
bottomSheetVerificationWaitingItem {
id("waiting")
- title(host.stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
+ title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName()))
}
}
is Success -> {
@@ -151,7 +148,7 @@ class VerificationRequestController @Inject constructor(
} else {
bottomSheetVerificationWaitingItem {
id("waiting")
- title(host.stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
+ title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName()))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt
index 6887451a76..a466759eae 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt
@@ -64,9 +64,7 @@ class VerificationRequestFragment :
}
override fun onClickOnVerificationStart(): Unit = withState(viewModel) { state ->
- state.otherUserMxItem?.id?.let { otherUserId ->
- viewModel.handle(VerificationAction.RequestVerificationByDM(otherUserId, state.roomId))
- }
+ viewModel.handle(VerificationAction.RequestVerificationByDM(state.otherUserId, state.roomId))
}
override fun onClickRecoverFromPassphrase() {
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 46b7efbb97..8fb73d6571 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -84,6 +84,7 @@ import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils
+import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -514,7 +515,7 @@ class HomeActivity :
)
}
- private fun promptSecurityEvent(userItem: MatrixItem.UserItem?, titleRes: Int, descRes: Int, action: ((VectorBaseActivity<*>) -> Unit)) {
+ private fun promptSecurityEvent(userItem: MatrixItem.UserItem, titleRes: Int, descRes: Int, action: ((VectorBaseActivity<*>) -> Unit)) {
popupAlertManager.postVectorAlert(
VerificationVectorAlert(
uid = "upgradeSecurity",
@@ -634,10 +635,18 @@ class HomeActivity :
launchInviteFriends()
true
}
+ R.id.menu_home_qr -> {
+ launchQrCode()
+ true
+ }
else -> false
}
}
+ private fun launchQrCode() {
+ startActivity(UserCodeActivity.newIntent(this, sharedActionViewModel.session.myUserId))
+ }
+
private fun launchInviteFriends() {
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink ->
analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends))
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
index e0b9e8ceb5..4147cf7186 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
@@ -20,13 +20,13 @@ import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.util.MatrixItem
sealed interface HomeActivityViewEvents : VectorViewEvents {
- data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents
+ data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
data class CurrentSessionNotVerified(
- val userItem: MatrixItem.UserItem?,
+ val userItem: MatrixItem.UserItem,
val waitForIncomingRequest: Boolean = true,
) : HomeActivityViewEvents
data class CurrentSessionCannotBeVerified(
- val userItem: MatrixItem.UserItem?,
+ val userItem: MatrixItem.UserItem,
) : HomeActivityViewEvents
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
object PromptToEnableSessionPush : HomeActivityViewEvents
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 6aba9eefcf..a08298e402 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -60,7 +60,7 @@ import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
@@ -119,17 +119,19 @@ class HomeActivityViewModel @AssistedInject constructor(
}
private fun observeReleaseNotes() = withState { state ->
- // we don't want to show release notes for new users or after relogin
- if (state.authenticationDescription == null && vectorPreferences.isNewAppLayoutEnabled()) {
- releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown ->
- if (!isAppLayoutOnboardingShown) {
- _viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes)
+ if (vectorPreferences.isNewAppLayoutEnabled()) {
+ // we don't want to show release notes for new users or after relogin
+ if (state.authenticationDescription == null) {
+ releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown ->
+ if (!isAppLayoutOnboardingShown) {
+ _viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes)
+ }
+ }.launchIn(viewModelScope)
+ } else {
+ // we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user)
+ viewModelScope.launch {
+ releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
}
- }.launchIn(viewModelScope)
- } else {
- // we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user)
- viewModelScope.launch {
- releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
}
}
}
@@ -312,10 +314,10 @@ class HomeActivityViewModel @AssistedInject constructor(
} else {
// cross signing keys have been reset
// Trigger a popup to re-verify
- // Note: user can be null in case of logout
- session.getUser(session.myUserId)
- ?.toMatrixItem()
- ?.let { user ->
+ // Note: user can be unknown in case of logout
+ session.getUserOrDefault(session.myUserId)
+ .toMatrixItem()
+ .let { user ->
_viewEvents.post(HomeActivityViewEvents.OnCrossSignedInvalidated(user))
}
}
@@ -396,7 +398,7 @@ class HomeActivityViewModel @AssistedInject constructor(
// New session
_viewEvents.post(
HomeActivityViewEvents.CurrentSessionNotVerified(
- session.getUser(session.myUserId)?.toMatrixItem(),
+ session.getUserOrDefault(session.myUserId).toMatrixItem(),
// Always send request instead of waiting for an incoming as per recent EW changes
false
)
@@ -404,7 +406,7 @@ class HomeActivityViewModel @AssistedInject constructor(
} else {
_viewEvents.post(
HomeActivityViewEvents.CurrentSessionCannotBeVerified(
- session.getUser(session.myUserId)?.toMatrixItem(),
+ session.getUserOrDefault(session.myUserId).toMatrixItem(),
)
)
}
@@ -424,7 +426,7 @@ class HomeActivityViewModel @AssistedInject constructor(
// Check this is not an SSO account
if (session.homeServerCapabilitiesService().getHomeServerCapabilities().canChangePassword) {
// Ask password to the user: Upgrade security
- _viewEvents.post(HomeActivityViewEvents.AskPasswordToInitCrossSigning(session.getUser(session.myUserId)?.toMatrixItem()))
+ _viewEvents.post(HomeActivityViewEvents.AskPasswordToInitCrossSigning(session.getUserOrDefault(session.myUserId).toMatrixItem()))
}
// Else (SSO) just ignore for the moment
} else {
diff --git a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
index 3681ba4c15..f31f8a7d92 100644
--- a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
@@ -201,13 +201,12 @@ class NewHomeDetailFragment :
private fun setupFabs() {
showFABs()
- views.newLayoutCreateChatButton.setOnClickListener {
- newChatBottomSheet.show(requireActivity().supportFragmentManager, NewChatBottomSheet.TAG)
+ views.newLayoutCreateChatButton.debouncedClicks {
+ newChatBottomSheet.takeIf { !it.isAdded }?.show(requireActivity().supportFragmentManager, NewChatBottomSheet.TAG)
}
- views.newLayoutOpenSpacesButton.setOnClickListener {
- // Click action for open spaces modal goes here
- spaceListBottomSheet.show(requireActivity().supportFragmentManager, SpaceListBottomSheet.TAG)
+ views.newLayoutOpenSpacesButton.debouncedClicks {
+ spaceListBottomSheet.takeIf { !it.isAdded }?.show(requireActivity().supportFragmentManager, SpaceListBottomSheet.TAG)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
index 34bdc5fcd3..855c47f4bb 100644
--- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt
@@ -21,10 +21,13 @@ import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import dagger.hilt.EntryPoints
import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
@@ -40,14 +43,14 @@ import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.flow.flow
import timber.log.Timber
data class UnknownDevicesState(
- val myMatrixItem: MatrixItem.UserItem? = null,
+ val myMatrixItem: MatrixItem.UserItem,
val unknownSessions: Async> = Uninitialized
) : MavericksState
@@ -73,7 +76,15 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
override fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel
}
- companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
+ companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() {
+ override fun initialState(viewModelContext: ViewModelContext): UnknownDevicesState {
+ val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
+
+ return UnknownDevicesState(
+ myMatrixItem = session.getUserOrDefault(session.myUserId).toMatrixItem()
+ )
+ }
+ }
private val ignoredDeviceList = ArrayList()
@@ -118,7 +129,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
.execute { async ->
// Timber.v("## Detector trigger passed distinct")
copy(
- myMatrixItem = session.getUser(session.myUserId)?.toMatrixItem(),
+ myMatrixItem = session.getUserOrDefault(session.myUserId).toMatrixItem(),
unknownSessions = async
)
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
index 3af849e965..399d5e0abe 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt
@@ -51,7 +51,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
object OpenRoomProfile : RoomDetailViewEvents()
data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents()
- object ShowWaitingView : RoomDetailViewEvents()
+ data class ShowWaitingView(val text: String? = null) : RoomDetailViewEvents()
object HideWaitingView : RoomDetailViewEvents()
data class DownloadFileState(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 8b6429abb1..5eb90dde4b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -493,7 +493,7 @@ class TimelineFragment :
is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
RoomDetailViewEvents.LeaveJitsiConference -> leaveJitsiConference()
- RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView()
+ is RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView(it.text)
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
index 535a949cd3..02dd2604e1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
@@ -39,6 +39,7 @@ import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.DecryptionFailureTracker
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
+import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.analytics.plan.JoinedRoom
import im.vector.app.features.call.conference.ConferenceEvent
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
@@ -78,12 +79,12 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixPatterns
+import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
-import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType
@@ -100,9 +101,11 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
+import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
@@ -185,6 +188,7 @@ class TimelineViewModel @AssistedInject constructor(
init {
// This method will take care of a null room to update the state.
observeRoomSummary()
+ observeLocalRoomSummary()
if (room == null) {
timeline = null
} else {
@@ -617,7 +621,7 @@ class TimelineViewModel @AssistedInject constructor(
}
private fun handleAddJitsiConference(action: RoomDetailAction.AddJitsiWidget) {
- _viewEvents.post(RoomDetailViewEvents.ShowWaitingView)
+ _viewEvents.post(RoomDetailViewEvents.ShowWaitingView())
viewModelScope.launch(Dispatchers.IO) {
try {
val widget = jitsiService.createJitsiWidget(initialState.roomId, action.withVideo)
@@ -637,7 +641,7 @@ class TimelineViewModel @AssistedInject constructor(
if (isJitsiWidget) {
setState { copy(jitsiState = jitsiState.copy(deleteWidgetInProgress = true)) }
} else {
- _viewEvents.post(RoomDetailViewEvents.ShowWaitingView)
+ _viewEvents.post(RoomDetailViewEvents.ShowWaitingView())
}
session.widgetService().destroyRoomWidget(initialState.roomId, widgetId)
// local echo
@@ -1231,6 +1235,32 @@ class TimelineViewModel @AssistedInject constructor(
}
}
+ private fun observeLocalRoomSummary() {
+ if (room != null && RoomLocalEcho.isLocalEchoId(room.roomId)) {
+ room.flow().liveLocalRoomSummary()
+ .unwrap()
+ .map { it.creationState }
+ .distinctUntilChanged()
+ .onEach { creationState ->
+ when (creationState) {
+ LocalRoomCreationState.NOT_CREATED -> Unit
+ LocalRoomCreationState.CREATING ->
+ _viewEvents.post(RoomDetailViewEvents.ShowWaitingView(stringProvider.getString(R.string.creating_direct_room)))
+ LocalRoomCreationState.FAILURE -> {
+ _viewEvents.post(RoomDetailViewEvents.HideWaitingView)
+ }
+ LocalRoomCreationState.CREATED -> {
+ room.localRoomSummary()?.let {
+ analyticsTracker.capture(CreatedRoom(isDM = it.roomSummary?.isDirect.orFalse()))
+ _viewEvents.post(RoomDetailViewEvents.OpenRoom(it.replacementRoomId!!, true))
+ }
+ }
+ }
+ }
+ .launchIn(viewModelScope)
+ }
+ }
+
private fun getUnreadState() {
if (room == null) return
combine(
@@ -1322,26 +1352,11 @@ class TimelineViewModel @AssistedInject constructor(
}
}
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)?.also {
- onRoomTombstoneUpdated(it)
+ setState { copy(tombstoneEvent = it) }
}
}
}
- private var roomTombstoneHandled = false
- private fun onRoomTombstoneUpdated(tombstoneEvent: Event) = withState { state ->
- if (roomTombstoneHandled) return@withState
- if (state.isLocalRoom()) {
- // Local room has been replaced, so navigate to the new room
- val roomId = tombstoneEvent.getClearContent()?.toModel()
- ?.replacementRoomId
- ?: return@withState
- _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId, closeCurrentRoom = true))
- roomTombstoneHandled = true
- } else {
- setState { copy(tombstoneEvent = tombstoneEvent) }
- }
- }
-
/**
* Navigates to the appropriate event (by paginating the thread timeline until the event is found
* in the snapshot. The main reason for this function is to support the /relations api
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListAction.kt
index e6b6b34503..aa982741f7 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListAction.kt
@@ -31,4 +31,5 @@ sealed class RoomListAction : VectorViewModelAction {
data class LeaveRoom(val roomId: String) : RoomListAction()
data class JoinSuggestedRoom(val roomId: String, val viaServers: List?) : RoomListAction()
data class ShowRoomDetails(val roomId: String, val viaServers: List?) : RoomListAction()
+ object DeleteAllLocalRoom : RoomListAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
index 2c876273ea..9591048725 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
@@ -149,10 +149,13 @@ class RoomListFragment :
(it.contentEpoxyController as? RoomSummaryPagedController)?.roomChangeMembershipStates = ms
}
}
- roomListViewModel.onEach(RoomListViewState::localRoomIds) {
- // Local rooms should not exist anymore when the room list is shown
- roomListViewModel.deleteLocalRooms(it)
- }
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ // Local rooms should not exist anymore when the room list is shown
+ roomListViewModel.handle(RoomListAction.DeleteAllLocalRoom)
}
private fun refreshCollapseStates() {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
index ca64a0f991..74b55d435d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt
@@ -47,7 +47,6 @@ import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
-import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
@@ -98,7 +97,6 @@ class RoomListViewModel @AssistedInject constructor(
init {
observeMembershipChanges()
- observeLocalRooms()
spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged()
@@ -126,23 +124,6 @@ class RoomListViewModel @AssistedInject constructor(
}
}
- private fun observeLocalRooms() {
- val queryParams = roomSummaryQueryParams {
- memberships = listOf(Membership.JOIN)
- }
- session
- .flow()
- .liveRoomSummaries(queryParams)
- .map { roomSummaries ->
- roomSummaries.mapNotNull { summary ->
- summary.roomId.takeIf { RoomLocalEcho.isLocalEchoId(it) }
- }.toSet()
- }
- .setOnEach { roomIds ->
- copy(localRoomIds = roomIds)
- }
- }
-
companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
private val roomListSectionBuilder = RoomListSectionBuilder(
@@ -174,6 +155,7 @@ class RoomListViewModel @AssistedInject constructor(
is RoomListAction.ToggleSection -> handleToggleSection(action.section)
is RoomListAction.JoinSuggestedRoom -> handleJoinSuggestedRoom(action)
is RoomListAction.ShowRoomDetails -> handleShowRoomDetails(action)
+ RoomListAction.DeleteAllLocalRoom -> handleDeleteLocalRooms()
}
}
@@ -181,14 +163,6 @@ class RoomListViewModel @AssistedInject constructor(
return session.getRoom(roomId)?.stateService()?.isPublic().orFalse()
}
- fun deleteLocalRooms(roomsIds: Set) {
- viewModelScope.launch {
- roomsIds.forEach {
- session.roomService().deleteLocalRoom(it)
- }
- }
- }
-
// PRIVATE METHODS *****************************************************************************
private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState {
@@ -346,4 +320,16 @@ class RoomListViewModel @AssistedInject constructor(
_viewEvents.post(value)
}
}
+
+ private fun handleDeleteLocalRooms() {
+ val localRoomIds = session.roomService()
+ .getRoomSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX) })
+ .map { it.roomId }
+
+ viewModelScope.launch {
+ localRoomIds.forEach {
+ session.roomService().deleteLocalRoom(it)
+ }
+ }
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt
index 3f46293346..d897225fd6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt
@@ -31,7 +31,6 @@ data class RoomListViewState(
val asyncSuggestedRooms: Async> = Uninitialized,
val currentUserName: String? = null,
val asyncSelectedSpace: Async = Uninitialized,
- val localRoomIds: Set = emptySet()
) : MavericksState {
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt
index 58ae6520cf..e6d162e8c3 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt
@@ -103,6 +103,9 @@ abstract class RoomSummaryItem : VectorEpoxyModel(R.layo
@EpoxyAttribute
var showSelected: Boolean = false
+ @EpoxyAttribute
+ var useSingleLineForLastEvent: Boolean = false
+
override fun bind(holder: Holder) {
super.bind(holder)
@@ -122,6 +125,10 @@ abstract class RoomSummaryItem : VectorEpoxyModel(R.layo
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
renderSelection(holder, showSelected)
holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
+
+ if (useSingleLineForLastEvent) {
+ holder.subtitleView.setLines(1)
+ }
}
private fun renderDisplayMode(holder: Holder) = when (displayMode) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
index 85879e6807..290b66e576 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
@@ -51,7 +51,8 @@ class RoomSummaryItemFactory @Inject constructor(
roomChangeMembershipStates: Map,
selectedRoomIds: Set,
displayMode: RoomListDisplayMode,
- listener: RoomListListener?
+ listener: RoomListListener?,
+ singleLineLastEvent: Boolean = false
): VectorEpoxyModel<*> {
return when (roomSummary.membership) {
Membership.INVITE -> {
@@ -59,7 +60,7 @@ class RoomSummaryItemFactory @Inject constructor(
createInvitationItem(roomSummary, changeMembershipState, listener)
}
else -> createRoomItem(
- roomSummary, selectedRoomIds, displayMode, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked }
+ roomSummary, selectedRoomIds, displayMode, singleLineLastEvent, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked }
)
}
}
@@ -118,8 +119,9 @@ class RoomSummaryItemFactory @Inject constructor(
roomSummary: RoomSummary,
selectedRoomIds: Set,
displayMode: RoomListDisplayMode,
+ singleLineLastEvent: Boolean,
onClick: ((RoomSummary) -> Unit)?,
- onLongClick: ((RoomSummary) -> Boolean)?
+ onLongClick: ((RoomSummary) -> Boolean)?,
): VectorEpoxyModel<*> {
val subtitle = getSearchResultSubtitle(roomSummary)
val unreadCount = roomSummary.notificationCount
@@ -140,7 +142,7 @@ class RoomSummaryItemFactory @Inject constructor(
} else {
createRoomSummaryItem(
roomSummary, displayMode, subtitle, latestEventTime, typingMessage,
- latestFormattedEvent, showHighlighted, showSelected, unreadCount, onClick, onLongClick
+ latestFormattedEvent, showHighlighted, showSelected, unreadCount, singleLineLastEvent, onClick, onLongClick
)
}
}
@@ -155,6 +157,7 @@ class RoomSummaryItemFactory @Inject constructor(
showHighlighted: Boolean,
showSelected: Boolean,
unreadCount: Int,
+ singleLineLastEvent: Boolean,
onClick: ((RoomSummary) -> Unit)?,
onLongClick: ((RoomSummary) -> Boolean)?
) = RoomSummaryItem_()
@@ -177,6 +180,7 @@ class RoomSummaryItemFactory @Inject constructor(
.unreadNotificationCount(unreadCount)
.hasUnreadMessage(roomSummary.hasUnreadMessages)
.hasDraft(roomSummary.userDrafts.isNotEmpty())
+ .useSingleLineForLastEvent(singleLineLastEvent)
.itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
.itemClickListener { onClick?.invoke(roomSummary) }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemPlaceHolder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemPlaceHolder.kt
index d4683f78a5..df191bc2ec 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemPlaceHolder.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemPlaceHolder.kt
@@ -16,6 +16,8 @@
package im.vector.app.features.home.room.list
+import android.widget.TextView
+import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
@@ -23,5 +25,18 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
@EpoxyModelClass
abstract class RoomSummaryItemPlaceHolder : VectorEpoxyModel(R.layout.item_room_placeholder) {
- class Holder : VectorEpoxyHolder()
+
+ @EpoxyAttribute
+ var useSingleLineForLastEvent: Boolean = false
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ if (useSingleLineForLastEvent) {
+ holder.subtitleView.setLines(1)
+ }
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val subtitleView by bind(R.id.subtitleView)
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryListController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryListController.kt
index 2eb8921fd5..a2b6ed51d9 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryListController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryListController.kt
@@ -17,18 +17,26 @@
package im.vector.app.features.home.room.list
import im.vector.app.features.home.RoomListDisplayMode
+import im.vector.app.features.settings.FontScalePreferences
import org.matrix.android.sdk.api.session.room.model.RoomSummary
class RoomSummaryListController(
private val roomSummaryItemFactory: RoomSummaryItemFactory,
- private val displayMode: RoomListDisplayMode
+ private val displayMode: RoomListDisplayMode,
+ fontScalePreferences: FontScalePreferences
) : CollapsableTypedEpoxyController>() {
var listener: RoomListListener? = null
+ private val shouldUseSingleLine: Boolean
+
+ init {
+ val fontScale = fontScalePreferences.getResolvedFontScaleValue()
+ shouldUseSingleLine = fontScale.scale > FontScalePreferences.SCALE_LARGE
+ }
override fun buildModels(data: List?) {
data?.forEach {
- add(roomSummaryItemFactory.create(it, emptyMap(), emptySet(), displayMode, listener))
+ add(roomSummaryItemFactory.create(it, emptyMap(), emptySet(), displayMode, listener, shouldUseSingleLine))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedController.kt
index 445438eec9..10d7ef425c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedController.kt
@@ -20,18 +20,26 @@ import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.core.utils.createUIHandler
import im.vector.app.features.home.RoomListDisplayMode
+import im.vector.app.features.settings.FontScalePreferences
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomSummary
class RoomSummaryPagedController(
private val roomSummaryItemFactory: RoomSummaryItemFactory,
- private val displayMode: RoomListDisplayMode
+ private val displayMode: RoomListDisplayMode,
+ fontScalePreferences: FontScalePreferences
) : PagedListEpoxyController(
// Important it must match the PageList builder notify Looper
modelBuildingHandler = createUIHandler()
), CollapsableControllerExtension {
var listener: RoomListListener? = null
+ private val shouldUseSingleLine: Boolean
+
+ init {
+ val fontScale = fontScalePreferences.getResolvedFontScaleValue()
+ shouldUseSingleLine = fontScale.scale > FontScalePreferences.SCALE_LARGE
+ }
var roomChangeMembershipStates: Map? = null
set(value) {
@@ -57,8 +65,14 @@ class RoomSummaryPagedController(
}
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
- // for place holder if enabled
- item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
- return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), displayMode, listener)
+ return if (item == null) {
+ val host = this
+ RoomSummaryItemPlaceHolder_().apply {
+ id(currentPosition)
+ useSingleLineForLastEvent(host.shouldUseSingleLine)
+ }
+ } else {
+ roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), displayMode, listener, shouldUseSingleLine)
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedControllerFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedControllerFactory.kt
index f72698048d..c5edd9c063 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedControllerFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryPagedControllerFactory.kt
@@ -17,18 +17,20 @@
package im.vector.app.features.home.room.list
import im.vector.app.features.home.RoomListDisplayMode
+import im.vector.app.features.settings.FontScalePreferences
import javax.inject.Inject
class RoomSummaryPagedControllerFactory @Inject constructor(
- private val roomSummaryItemFactory: RoomSummaryItemFactory
+ private val roomSummaryItemFactory: RoomSummaryItemFactory,
+ private val fontScalePreferences: FontScalePreferences
) {
fun createRoomSummaryPagedController(displayMode: RoomListDisplayMode): RoomSummaryPagedController {
- return RoomSummaryPagedController(roomSummaryItemFactory, displayMode)
+ return RoomSummaryPagedController(roomSummaryItemFactory, displayMode, fontScalePreferences)
}
fun createRoomSummaryListController(displayMode: RoomListDisplayMode): RoomSummaryListController {
- return RoomSummaryListController(roomSummaryItemFactory, displayMode)
+ return RoomSummaryListController(roomSummaryItemFactory, displayMode, fontScalePreferences)
}
fun createSuggestedRoomListController(): SuggestedRoomListController {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeFilteredRoomsController.kt
similarity index 70%
rename from vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt
rename to vector/src/main/java/im/vector/app/features/home/room/list/home/HomeFilteredRoomsController.kt
index 789c9e9985..2b4a514750 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeFilteredRoomsController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeFilteredRoomsController.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.list.home.filter
+package im.vector.app.features.home.room.list.home
+import androidx.paging.PagedList
import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.core.platform.StateView
@@ -24,12 +25,14 @@ import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.list.RoomListListener
import im.vector.app.features.home.room.list.RoomSummaryItemFactory
import im.vector.app.features.home.room.list.RoomSummaryItemPlaceHolder_
-import im.vector.app.features.home.room.list.home.roomListEmptyItem
+import im.vector.app.features.settings.FontScalePreferences
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import javax.inject.Inject
-class HomeFilteredRoomsController(
+class HomeFilteredRoomsController @Inject constructor(
private val roomSummaryItemFactory: RoomSummaryItemFactory,
+ fontScalePreferences: FontScalePreferences
) : PagedListEpoxyController(
// Important it must match the PageList builder notify Looper
modelBuildingHandler = createUIHandler()
@@ -43,22 +46,18 @@ class HomeFilteredRoomsController(
}
var listener: RoomListListener? = null
- var onFilterChanged: ((HomeRoomFilter) -> Unit)? = null
- private var filtersData: List? = null
private var emptyStateData: StateView.State.Empty? = null
private var currentState: StateView.State = StateView.State.Content
- override fun addModels(models: List>) {
- val host = this
- if (host.filtersData != null) {
- roomFilterHeaderItem {
- id("filter_header")
- filtersData(host.filtersData)
- onFilterChangedListener(host.onFilterChanged)
- }
- }
+ private val shouldUseSingleLine: Boolean
+ init {
+ val fontScale = fontScalePreferences.getResolvedFontScaleValue()
+ shouldUseSingleLine = fontScale.scale > FontScalePreferences.SCALE_LARGE
+ }
+
+ override fun addModels(models: List>) {
if (models.isEmpty() && emptyStateData != null) {
emptyStateData?.let { emptyState ->
roomListEmptyItem {
@@ -77,12 +76,22 @@ class HomeFilteredRoomsController(
this.emptyStateData = state
}
- fun submitFiltersData(data: List?) {
- this.filtersData = data
- requestForcedModelBuild()
+ fun submitPagedList(newList: PagedList) {
+ submitList(newList)
+ if (newList.isEmpty()) {
+ requestForcedModelBuild()
+ }
}
+
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
- item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
- return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
+ return if (item == null) {
+ val host = this
+ RoomSummaryItemPlaceHolder_().apply {
+ id(currentPosition)
+ useSingleLineForLastEvent(host.shouldUseSingleLine)
+ }
+ } else {
+ roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener, shouldUseSingleLine)
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt
index 6d17792969..5760874812 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListAction.kt
@@ -17,7 +17,7 @@
package im.vector.app.features.home.room.list.home
import im.vector.app.core.platform.VectorViewModelAction
-import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
+import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
@@ -27,4 +27,5 @@ sealed class HomeRoomListAction : VectorViewModelAction {
data class ToggleTag(val roomId: String, val tag: String) : HomeRoomListAction()
data class LeaveRoom(val roomId: String) : HomeRoomListAction()
data class ChangeRoomFilter(val filter: HomeRoomFilter) : HomeRoomListAction()
+ object DeleteAllLocalRoom : HomeRoomListAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
index edb619cd90..9b8a686f37 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt
@@ -24,8 +24,6 @@ import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.airbnb.epoxy.EpoxyControllerAdapter
import com.airbnb.epoxy.OnModelBuildFinishedListener
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@@ -37,19 +35,17 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.UserPreferencesProvider
+import im.vector.app.core.utils.FirstItemUpdatedObserver
import im.vector.app.databinding.FragmentRoomListBinding
import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.home.room.list.RoomListAnimator
import im.vector.app.features.home.room.list.RoomListListener
-import im.vector.app.features.home.room.list.RoomSummaryItemFactory
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
-import im.vector.app.features.home.room.list.home.filter.HomeFilteredRoomsController
-import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
+import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
+import im.vector.app.features.home.room.list.home.header.HomeRoomsHeadersController
import im.vector.app.features.home.room.list.home.invites.InvitesActivity
-import im.vector.app.features.home.room.list.home.invites.InvitesCounterController
-import im.vector.app.features.home.room.list.home.recent.RecentRoomCarouselController
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -63,14 +59,14 @@ class HomeRoomListFragment :
VectorBaseFragment(),
RoomListListener {
- @Inject lateinit var roomSummaryItemFactory: RoomSummaryItemFactory
@Inject lateinit var userPreferencesProvider: UserPreferencesProvider
- @Inject lateinit var recentRoomCarouselController: RecentRoomCarouselController
- @Inject lateinit var invitesCounterController: InvitesCounterController
+ @Inject lateinit var headersController: HomeRoomsHeadersController
+ @Inject lateinit var roomsController: HomeFilteredRoomsController
private val roomListViewModel: HomeRoomListViewModel by fragmentViewModel()
private lateinit var sharedQuickActionsViewModel: RoomListQuickActionsSharedActionViewModel
private var concatAdapter = ConcatAdapter()
+ private lateinit var firstItemObserver: FirstItemUpdatedObserver
private var modelBuildListener: OnModelBuildFinishedListener? = null
private lateinit var stateRestorer: LayoutManagerStateRestorer
@@ -87,6 +83,13 @@ class HomeRoomListFragment :
setupRecyclerView()
}
+ override fun onStart() {
+ super.onStart()
+
+ // Local rooms should not exist anymore when the room list is shown
+ roomListViewModel.handle(HomeRoomListAction.DeleteAllLocalRoom)
+ }
+
private fun setupObservers() {
sharedQuickActionsViewModel = activityViewModelProvider[RoomListQuickActionsSharedActionViewModel::class.java]
sharedQuickActionsViewModel
@@ -128,7 +131,6 @@ class HomeRoomListFragment :
roomListViewModel.handle(HomeRoomListAction.ToggleTag(quickAction.roomId, RoomTag.ROOM_TAG_LOW_PRIORITY))
}
is RoomListQuickActionsSharedAction.Leave -> {
- roomListViewModel.handle(HomeRoomListAction.LeaveRoom(quickAction.roomId))
promptLeaveRoom(quickAction.roomId)
}
}
@@ -136,6 +138,9 @@ class HomeRoomListFragment :
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
+ firstItemObserver = FirstItemUpdatedObserver(layoutManager) {
+ layoutManager.scrollToPosition(0)
+ }
stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
views.roomListView.layoutManager = layoutManager
views.roomListView.itemAnimator = RoomListAnimator()
@@ -143,33 +148,48 @@ class HomeRoomListFragment :
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
- roomListViewModel.sections.onEach { sections ->
- setUpAdapters(sections)
+ roomListViewModel.onEach(HomeRoomListViewState::headersData) {
+ headersController.submitData(it)
+ }
+
+ roomListViewModel.onEach(HomeRoomListViewState::roomsPagedList) { roomsList ->
+ roomsList?.let {
+ roomsController.submitPagedList(it)
+ if (it.isEmpty()) {
+ roomsController.requestForcedModelBuild()
+ }
+ }
+ }
+
+ roomListViewModel.emptyStateFlow.onEach { emptyStateOptional ->
+ roomsController.submitEmptyStateData(emptyStateOptional.getOrNull())
}.launchIn(lifecycleScope)
+ setUpAdapters()
+
views.roomListView.adapter = concatAdapter
- // we need to force scroll when recents/filter tabs are added to make them visible
- concatAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
- override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
- if (positionStart == 0) {
- layoutManager.scrollToPosition(0)
- }
- }
- })
+ concatAdapter.registerAdapterDataObserver(firstItemObserver)
}
override fun invalidate() = withState(roomListViewModel) { state ->
views.stateView.state = state.state
}
- private fun setUpAdapters(sections: Set) {
- concatAdapter.adapters.forEach {
- concatAdapter.removeAdapter(it)
- }
- sections.forEach {
- concatAdapter.addAdapter(getAdapterForData(it))
- }
+ private fun setUpAdapters() {
+ val headersAdapter = headersController.also { controller ->
+ controller.invitesClickListener = ::onInvitesCounterClicked
+ controller.onFilterChangedListener = ::onRoomFilterChanged
+ controller.recentsRoomListener = this
+ }.adapter
+
+ val roomsAdapter = roomsController
+ .also { controller ->
+ controller.listener = this
+ }.adapter
+
+ concatAdapter.addAdapter(headersAdapter)
+ concatAdapter.addAdapter(roomsAdapter)
}
private fun promptLeaveRoom(roomId: String) {
@@ -191,43 +211,6 @@ class HomeRoomListFragment :
.show()
}
- private fun getAdapterForData(section: HomeRoomSection): EpoxyControllerAdapter {
- return when (section) {
- is HomeRoomSection.RoomSummaryData -> {
- HomeFilteredRoomsController(
- roomSummaryItemFactory,
- ).also { controller ->
- controller.listener = this
- controller.onFilterChanged = ::onRoomFilterChanged
- roomListViewModel.emptyStateFlow.onEach { emptyStateOptional ->
- controller.submitEmptyStateData(emptyStateOptional.getOrNull())
- }.launchIn(lifecycleScope)
- section.filtersData.onEach {
- controller.submitFiltersData(it.getOrNull())
- }.launchIn(lifecycleScope)
- section.list.observe(viewLifecycleOwner) { list ->
- controller.submitList(list)
- if (list.isEmpty()) {
- controller.requestForcedModelBuild()
- }
- }
- }.adapter
- }
- is HomeRoomSection.RecentRoomsData -> recentRoomCarouselController.also { controller ->
- controller.listener = this
- section.list.observe(viewLifecycleOwner) { list ->
- controller.submitList(list)
- }
- }.adapter
- is HomeRoomSection.InvitesCountData -> invitesCounterController.also { controller ->
- controller.clickListener = ::onInvitesCounterClicked
- section.count.observe(viewLifecycleOwner) { count ->
- controller.submitData(count)
- }
- }.adapter
- }
- }
-
private fun onInvitesCounterClicked() {
startActivity(Intent(activity, InvitesActivity::class.java))
}
@@ -247,8 +230,15 @@ class HomeRoomListFragment :
override fun onDestroyView() {
views.roomListView.cleanup()
- recentRoomCarouselController.listener = null
- invitesCounterController.clickListener = null
+
+ headersController.recentsRoomListener = null
+ headersController.invitesClickListener = null
+ headersController.onFilterChangedListener = null
+
+ roomsController.listener = null
+
+ concatAdapter.unregisterAdapterDataObserver(firstItemObserver)
+
super.onDestroyView()
}
@@ -266,21 +256,13 @@ class HomeRoomListFragment :
return true
}
- override fun onRejectRoomInvitation(room: RoomSummary) {
- TODO("Not yet implemented")
- }
+ override fun onRejectRoomInvitation(room: RoomSummary) = Unit
- override fun onAcceptRoomInvitation(room: RoomSummary) {
- TODO("Not yet implemented")
- }
+ override fun onAcceptRoomInvitation(room: RoomSummary) = Unit
- override fun onJoinSuggestedRoom(room: SpaceChildInfo) {
- TODO("Not yet implemented")
- }
+ override fun onJoinSuggestedRoom(room: SpaceChildInfo) = Unit
- override fun onSuggestedRoomClicked(room: SpaceChildInfo) {
- TODO("Not yet implemented")
- }
+ override fun onSuggestedRoomClicked(room: SpaceChildInfo) = Unit
// endregion
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
index b52c4e0190..ad2656cec1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt
@@ -17,7 +17,7 @@
package im.vector.app.features.home.room.list.home
import android.widget.ImageView
-import androidx.lifecycle.map
+import androidx.lifecycle.asFlow
import androidx.paging.PagedList
import arrow.core.toOption
import com.airbnb.mvrx.MavericksViewModelFactory
@@ -32,15 +32,18 @@ import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider
-import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
+import im.vector.app.features.displayname.getBestName
+import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -53,16 +56,20 @@ import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.query.toActiveSpaceOrNoFilter
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.state.isPublic
import org.matrix.android.sdk.api.util.Optional
+import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.flow.flow
+import java.util.concurrent.CancellationException
class HomeRoomListViewModel @AssistedInject constructor(
@Assisted initialState: HomeRoomListViewState,
@@ -87,128 +94,86 @@ class HomeRoomListViewModel @AssistedInject constructor(
.setPrefetchDistance(10)
.build()
- private val _sections = MutableSharedFlow>(replay = 1)
- val sections = _sections.asSharedFlow()
-
private var currentFilter: HomeRoomFilter = HomeRoomFilter.ALL
private val _emptyStateFlow = MutableSharedFlow>(replay = 1)
val emptyStateFlow = _emptyStateFlow.asSharedFlow()
+ private var roomsFlowJob: Job? = null
+
private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null
init {
- configureSections()
- observePreferences()
+ observeOrderPreferences()
+ observeInvites()
+ observeRecents()
+ observeFilterTabs()
+ observeRooms()
}
- private fun observePreferences() {
- preferencesStore.areRecentsEnabledFlow.onEach {
- configureSections()
- }.launchIn(viewModelScope)
-
- preferencesStore.isAZOrderingEnabledFlow.onEach {
- configureSections()
- }.launchIn(viewModelScope)
- }
-
- private fun configureSections() = viewModelScope.launch {
- val newSections = mutableSetOf()
- newSections.add(getInvitesCountSection())
-
- val areSettingsEnabled = preferencesStore.areRecentsEnabledFlow.first()
- if (areSettingsEnabled) {
- newSections.add(getRecentRoomsSection())
- }
- newSections.add(getFilteredRoomsSection())
-
- emitEmptyState()
- _sections.emit(newSections)
-
- setState {
- copy(state = StateView.State.Content)
- }
- }
-
- private fun getRecentRoomsSection(): HomeRoomSection {
- val liveList = session.roomService()
- .getBreadcrumbsLive(roomSummaryQueryParams {
- displayName = QueryStringValue.NoCondition
- memberships = listOf(Membership.JOIN)
- })
-
- return HomeRoomSection.RecentRoomsData(
- list = liveList
- )
- }
-
- private fun getInvitesCountSection(): HomeRoomSection.InvitesCountData {
- val builder = RoomSummaryQueryParams.Builder().also {
- it.memberships = listOf(Membership.INVITE)
- }
-
- val liveCount = session.roomService().getRoomSummariesLive(
- builder.build(),
- RoomSortOrder.ACTIVITY
- ).map { it.count() }
-
- return HomeRoomSection.InvitesCountData(liveCount)
- }
-
- private suspend fun getFilteredRoomsSection(): HomeRoomSection.RoomSummaryData {
- val builder = RoomSummaryQueryParams.Builder().also {
- it.memberships = listOf(Membership.JOIN)
- }
-
- val params = getFilteredQueryParams(HomeRoomFilter.ALL, builder.build())
- val sortOrder = if (preferencesStore.isAZOrderingEnabledFlow.first()) {
- RoomSortOrder.NAME
- } else {
- RoomSortOrder.ACTIVITY
- }
-
- val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
- params,
- pagedListConfig,
- sortOrder
- ).also {
- this.filteredPagedRoomSummariesLive = it
- }
-
- spaceStateHandler.getSelectedSpaceFlow()
- .distinctUntilChanged()
- .onStart {
- emit(spaceStateHandler.getCurrentSpace().toOption())
- }
- .onEach { selectedSpaceOption ->
- val selectedSpace = selectedSpaceOption.orNull()
- liveResults.queryParams = liveResults.queryParams.copy(
- spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
- )
- emitEmptyState()
+ private fun observeInvites() {
+ session.flow()
+ .liveRoomSummaries(
+ roomSummaryQueryParams {
+ memberships = listOf(Membership.INVITE)
+ },
+ RoomSortOrder.ACTIVITY
+ ).onEach { list ->
+ setState { copy(headersData = headersData.copy(invitesCount = list.size)) }
}.launchIn(viewModelScope)
-
- return HomeRoomSection.RoomSummaryData(
- list = liveResults.livePagedList,
- filtersData = getFiltersDataFlow()
- )
}
- private fun emitEmptyState() {
- viewModelScope.launch {
- val emptyState = getEmptyStateData(currentFilter, spaceStateHandler.getCurrentSpace())
- _emptyStateFlow.emit(Optional.from(emptyState))
+ private fun observeRecents() {
+ preferencesStore.areRecentsEnabledFlow
+ .distinctUntilChanged()
+ .flatMapLatest { areEnabled ->
+ if (areEnabled) {
+ session.flow()
+ .liveBreadcrumbs(roomSummaryQueryParams {
+ memberships = listOf(Membership.JOIN)
+ })
+ .map { Optional.from(it) }
+ } else {
+ flow { emit(Optional.empty()) }
+ }.onEach { listOptional ->
+ setState { copy(headersData = headersData.copy(recents = listOptional.getOrNull())) }
+ }
+ }.launchIn(viewModelScope)
+ }
+
+ private fun observeFilterTabs() {
+ preferencesStore.areFiltersEnabledFlow
+ .distinctUntilChanged()
+ .flatMapLatest { areEnabled ->
+ if (areEnabled) {
+ getFilterTabsFlow()
+ } else {
+ flow { emit(Optional.empty()) }
+ }.onEach { filtersOptional ->
+ setState {
+ validateCurrentFilter(filtersOptional.getOrNull())
+ copy(
+ headersData = headersData.copy(
+ filtersList = filtersOptional.getOrNull(),
+ currentFilter = currentFilter
+ )
+ )
+ }
+ }
+ }.launchIn(viewModelScope)
+ }
+
+ private fun validateCurrentFilter(filtersList: List?) {
+ if (filtersList?.contains(currentFilter) != true) {
+ handleChangeRoomFilter(HomeRoomFilter.ALL)
}
}
- private fun getFiltersDataFlow(): SharedFlow>> {
- val flow = MutableSharedFlow>>(replay = 1)
-
+ private fun getFilterTabsFlow(): Flow>> {
val spaceFLow = spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged()
.onStart {
emit(spaceStateHandler.getCurrentSpace().toOption())
}
-
val favouritesFlow =
spaceFLow.flatMapLatest { selectedSpace ->
session.flow()
@@ -236,31 +201,82 @@ class HomeRoomListViewModel @AssistedInject constructor(
.map { it.isNotEmpty() }
.distinctUntilChanged()
- combine(favouritesFlow, dmsFLow, preferencesStore.areFiltersEnabledFlow) { hasFavourite, hasDm, areFiltersEnabled ->
- Triple(hasFavourite, hasDm, areFiltersEnabled)
- }.onEach { (hasFavourite, hasDm, areFiltersEnabled) ->
- if (areFiltersEnabled) {
- val filtersData = mutableListOf(
- HomeRoomFilter.ALL,
- HomeRoomFilter.UNREADS
+ return combine(favouritesFlow, dmsFLow) { hasFavourite, hasDm ->
+ hasFavourite to hasDm
+ }.map { (hasFavourite, hasDm) ->
+ val filtersData = mutableListOf(
+ HomeRoomFilter.ALL,
+ HomeRoomFilter.UNREADS
+ )
+ if (hasFavourite) {
+ filtersData.add(
+ HomeRoomFilter.FAVOURITES
)
- if (hasFavourite) {
- filtersData.add(
- HomeRoomFilter.FAVOURITES
- )
- }
- if (hasDm) {
- filtersData.add(
- HomeRoomFilter.PEOPlE
- )
- }
- flow.emit(Optional.from(filtersData))
- } else {
- flow.emit(Optional.empty())
}
- }.launchIn(viewModelScope)
+ if (hasDm) {
+ filtersData.add(
+ HomeRoomFilter.PEOPlE
+ )
+ }
+ Optional.from(filtersData)
+ }
+ }
- return flow
+ private fun observeRooms() = viewModelScope.launch {
+ val builder = RoomSummaryQueryParams.Builder().also {
+ it.memberships = listOf(Membership.JOIN)
+ }
+
+ val params = getFilteredQueryParams(currentFilter, builder.build())
+ val sortOrder = if (preferencesStore.isAZOrderingEnabledFlow.first()) {
+ RoomSortOrder.NAME
+ } else {
+ RoomSortOrder.ACTIVITY
+ }
+
+ val liveResults = session.roomService().getFilteredPagedRoomSummariesLive(
+ params,
+ pagedListConfig,
+ sortOrder
+ ).also {
+ filteredPagedRoomSummariesLive = it
+ }
+
+ spaceStateHandler.getSelectedSpaceFlow()
+ .distinctUntilChanged()
+ .onStart {
+ emit(spaceStateHandler.getCurrentSpace().toOption())
+ }
+ .onEach { selectedSpaceOption ->
+ val selectedSpace = selectedSpaceOption.orNull()
+ liveResults.queryParams = liveResults.queryParams.copy(
+ spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
+ )
+ emitEmptyState()
+ }
+ .launchIn(viewModelScope)
+
+ roomsFlowJob?.cancel(CancellationException())
+
+ roomsFlowJob = liveResults.livePagedList
+ .asFlow()
+ .onEach {
+ setState { copy(roomsPagedList = it) }
+ }
+ .launchIn(viewModelScope)
+ }
+
+ private fun observeOrderPreferences() {
+ preferencesStore.isAZOrderingEnabledFlow.onEach {
+ observeRooms()
+ }.launchIn(viewModelScope)
+ }
+
+ private fun emitEmptyState() {
+ viewModelScope.launch {
+ val emptyState = getEmptyStateData(currentFilter, spaceStateHandler.getCurrentSpace())
+ _emptyStateFlow.emit(Optional.from(emptyState))
+ }
}
private fun getFilteredQueryParams(filter: HomeRoomFilter, currentParams: RoomSummaryQueryParams): RoomSummaryQueryParams {
@@ -296,7 +312,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
isBigImage = true
)
} else {
- val userName = session.userService().getUser(session.myUserId)?.displayName ?: ""
+ val userName = session.getUserOrDefault(session.myUserId).toMatrixItem().getBestName()
StateView.State.Empty(
title = stringProvider.getString(R.string.home_empty_no_rooms_title, userName),
message = stringProvider.getString(R.string.home_empty_no_rooms_message),
@@ -323,16 +339,21 @@ class HomeRoomListViewModel @AssistedInject constructor(
is HomeRoomListAction.LeaveRoom -> handleLeaveRoom(action)
is HomeRoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
is HomeRoomListAction.ToggleTag -> handleToggleTag(action)
- is HomeRoomListAction.ChangeRoomFilter -> handleChangeRoomFilter(action)
+ is HomeRoomListAction.ChangeRoomFilter -> handleChangeRoomFilter(action.filter)
+ HomeRoomListAction.DeleteAllLocalRoom -> handleDeleteLocalRooms()
}
}
- private fun handleChangeRoomFilter(action: HomeRoomListAction.ChangeRoomFilter) {
- currentFilter = action.filter
+ private fun handleChangeRoomFilter(newFilter: HomeRoomFilter) {
+ if (currentFilter == newFilter) {
+ return
+ }
+ currentFilter = newFilter
filteredPagedRoomSummariesLive?.let { liveResults ->
- liveResults.queryParams = getFilteredQueryParams(action.filter, liveResults.queryParams)
+ liveResults.queryParams = getFilteredQueryParams(currentFilter, liveResults.queryParams)
}
+ setState { copy(headersData = headersData.copy(currentFilter = currentFilter)) }
emitEmptyState()
}
@@ -390,6 +411,18 @@ class HomeRoomListViewModel @AssistedInject constructor(
}
}
+ private fun handleDeleteLocalRooms() = withState {
+ val localRoomIds = session.roomService()
+ .getRoomSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX) })
+ .map { it.roomId }
+
+ viewModelScope.launch {
+ localRoomIds.forEach {
+ session.roomService().deleteLocalRoom(it)
+ }
+ }
+ }
+
private fun String.otherTag(): String? {
return when (this) {
RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt
index bfcaea22e9..db3a57e63e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewState.kt
@@ -16,9 +16,14 @@
package im.vector.app.features.home.room.list.home
+import androidx.paging.PagedList
import com.airbnb.mvrx.MavericksState
import im.vector.app.core.platform.StateView
+import im.vector.app.features.home.room.list.home.header.RoomsHeadersData
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
data class HomeRoomListViewState(
- val state: StateView.State = StateView.State.Loading
+ val state: StateView.State = StateView.State.Content,
+ val headersData: RoomsHeadersData = RoomsHeadersData(),
+ val roomsPagedList: PagedList? = null,
) : MavericksState
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt
deleted file mode 100644
index 29df594d06..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomSection.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * 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 im.vector.app.features.home.room.list.home
-
-import androidx.lifecycle.LiveData
-import androidx.paging.PagedList
-import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
-import kotlinx.coroutines.flow.SharedFlow
-import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import org.matrix.android.sdk.api.util.Optional
-
-sealed class HomeRoomSection {
- data class RoomSummaryData(
- val list: LiveData>,
- val filtersData: SharedFlow>>,
- ) : HomeRoomSection()
-
- data class RecentRoomsData(
- val list: LiveData>
- ) : HomeRoomSection()
-
- data class InvitesCountData(
- val count: LiveData
- ) : HomeRoomSection()
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt
index 05b86f7393..3f12ae4a63 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/NewChatBottomSheet.kt
@@ -16,43 +16,53 @@
package im.vector.app.features.home.room.list.home
+import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.FragmentNewChatBottomSheetBinding
import im.vector.app.features.navigation.Navigator
import javax.inject.Inject
@AndroidEntryPoint
-class NewChatBottomSheet @Inject constructor() : BottomSheetDialogFragment() {
+class NewChatBottomSheet : VectorBaseBottomSheetDialogFragment() {
@Inject lateinit var navigator: Navigator
- private lateinit var binding: FragmentNewChatBottomSheetBinding
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentNewChatBottomSheetBinding {
+ return FragmentNewChatBottomSheetBinding.inflate(inflater, container, false)
+ }
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- binding = FragmentNewChatBottomSheetBinding.inflate(inflater, container, false)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initFABs()
- return binding.root
}
private fun initFABs() {
- binding.startChat.setOnClickListener {
+ views.startChat.debouncedClicks {
+ dismiss()
navigator.openCreateDirectRoom(requireActivity())
}
- binding.createRoom.setOnClickListener {
+ views.createRoom.debouncedClicks {
+ dismiss()
navigator.openCreateRoom(requireActivity())
}
- binding.exploreRooms.setOnClickListener {
+ views.exploreRooms.debouncedClicks {
+ dismiss()
navigator.openRoomDirectory(requireContext())
}
}
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return super.onCreateDialog(savedInstanceState).apply {
+ setPeekHeightAsScreenPercentage(0.5f)
+ }
+ }
+
companion object {
const val TAG = "NewChatBottomSheet"
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeRoomFilter.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomFilter.kt
similarity index 94%
rename from vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeRoomFilter.kt
rename to vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomFilter.kt
index ce33440238..9bd800a47c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/HomeRoomFilter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.list.home.filter
+package im.vector.app.features.home.room.list.home.header
import androidx.annotation.StringRes
import im.vector.app.R
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt
new file mode 100644
index 0000000000..56cccd9c36
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.home.room.list.home.header
+
+import android.content.res.Resources
+import android.util.TypedValue
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.airbnb.epoxy.Carousel
+import com.airbnb.epoxy.CarouselModelBuilder
+import com.airbnb.epoxy.EpoxyController
+import com.airbnb.epoxy.EpoxyModel
+import com.airbnb.epoxy.carousel
+import com.google.android.material.color.MaterialColors
+import im.vector.app.R
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.core.utils.FirstItemUpdatedObserver
+import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.list.RoomListListener
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.util.toMatrixItem
+import javax.inject.Inject
+
+class HomeRoomsHeadersController @Inject constructor(
+ val stringProvider: StringProvider,
+ private val avatarRenderer: AvatarRenderer,
+ resources: Resources,
+) : EpoxyController() {
+
+ private var data: RoomsHeadersData = RoomsHeadersData()
+
+ var onFilterChangedListener: ((HomeRoomFilter) -> Unit)? = null
+ var recentsRoomListener: RoomListListener? = null
+ var invitesClickListener: (() -> Unit)? = null
+
+ private var carousel: Carousel? = null
+
+ private var carouselAdapterObserver: FirstItemUpdatedObserver? = null
+
+ private val recentsHPadding = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 4f,
+ resources.displayMetrics
+ ).toInt()
+
+ private val recentsTopPadding = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 12f,
+ resources.displayMetrics
+ ).toInt()
+
+ override fun buildModels() {
+ val host = this
+ if (data.invitesCount != 0) {
+ addInviteCounter(host.invitesClickListener, data.invitesCount)
+ }
+
+ data.recents?.let {
+ addRecents(host, it)
+ }
+
+ host.data.filtersList?.let {
+ addRoomFilterHeaderItem(host.onFilterChangedListener, it, host.data.currentFilter)
+ }
+ }
+
+ private fun addInviteCounter(invitesClickListener: (() -> Unit)?, invitesCount: Int) {
+ inviteCounterItem {
+ id("invites_counter")
+ invitesCount(invitesCount)
+ listener { invitesClickListener?.invoke() }
+ }
+ }
+
+ private fun addRecents(host: HomeRoomsHeadersController, recents: List) {
+ carousel {
+ id("recents_carousel")
+ padding(
+ Carousel.Padding(
+ host.recentsHPadding,
+ host.recentsTopPadding,
+ host.recentsHPadding,
+ 0,
+ 0,
+ )
+ )
+ onBind { _, view, _ ->
+ host.carousel = view
+ host.unsubscribeAdapterObserver()
+ host.subscribeAdapterObserver()
+
+ val colorSurface = MaterialColors.getColor(view, R.attr.vctr_toolbar_background)
+ view.setBackgroundColor(colorSurface)
+ }
+
+ onUnbind { _, _ ->
+ host.carousel = null
+ host.unsubscribeAdapterObserver()
+ }
+
+ withModelsFrom(recents) { roomSummary ->
+ val onClick = host.recentsRoomListener?.let { it::onRoomClicked }
+ val onLongClick = host.recentsRoomListener?.let { it::onRoomLongClicked }
+
+ RecentRoomItem_()
+ .id(roomSummary.roomId)
+ .avatarRenderer(host.avatarRenderer)
+ .matrixItem(roomSummary.toMatrixItem())
+ .unreadNotificationCount(roomSummary.notificationCount)
+ .showHighlighted(roomSummary.highlightCount > 0)
+ .itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
+ .itemClickListener { onClick?.invoke(roomSummary) }
+ }
+ }
+ }
+
+ private fun unsubscribeAdapterObserver() {
+ carouselAdapterObserver?.let { observer ->
+ try {
+ carousel?.adapter?.unregisterAdapterDataObserver(observer)
+ carouselAdapterObserver = null
+ } catch (e: IllegalStateException) {
+ // do nothing
+ }
+ }
+ }
+
+ private fun subscribeAdapterObserver() {
+ (carousel?.layoutManager as? LinearLayoutManager)?.let { layoutManager ->
+ carouselAdapterObserver = FirstItemUpdatedObserver(layoutManager) {
+ carousel?.post {
+ layoutManager.scrollToPosition(0)
+ }
+ }.also { observer ->
+ try {
+ carousel?.adapter?.registerAdapterDataObserver(observer)
+ } catch (e: IllegalStateException) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ private fun addRoomFilterHeaderItem(
+ filterChangedListener: ((HomeRoomFilter) -> Unit)?,
+ filtersList: List,
+ currentFilter: HomeRoomFilter?,
+ ) {
+ roomFilterHeaderItem {
+ id("filter_header")
+ filtersData(filtersList)
+ selectedFilter(currentFilter)
+ onFilterChangedListener(filterChangedListener)
+ }
+ }
+
+ fun submitData(data: RoomsHeadersData) {
+ this.data = data
+ requestModelBuild()
+ }
+}
+
+private inline fun CarouselModelBuilder.withModelsFrom(
+ items: List,
+ modelBuilder: (T) -> EpoxyModel<*>
+) {
+ models(items.map { modelBuilder(it) })
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InviteCounterItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/InviteCounterItem.kt
similarity index 96%
rename from vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InviteCounterItem.kt
rename to vector/src/main/java/im/vector/app/features/home/room/list/home/header/InviteCounterItem.kt
index 7536bde10a..5c43c86933 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InviteCounterItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/InviteCounterItem.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.list.home.invites
+package im.vector.app.features.home.room.list.home.header
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RecentRoomItem.kt
similarity index 98%
rename from vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomItem.kt
rename to vector/src/main/java/im/vector/app/features/home/room/list/home/header/RecentRoomItem.kt
index d7c72ba5ed..9cd28624a5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RecentRoomItem.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.list.home.recent
+package im.vector.app.features.home.room.list.home.header
import android.view.HapticFeedbackConstants
import android.view.View
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/RoomFilterHeaderItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt
similarity index 85%
rename from vector/src/main/java/im/vector/app/features/home/room/list/home/filter/RoomFilterHeaderItem.kt
rename to vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt
index bbe503806b..ed99b51681 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/filter/RoomFilterHeaderItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package im.vector.app.features.home.room.list.home.filter
+package im.vector.app.features.home.room.list.home.header
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
@@ -32,13 +32,20 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel? = null
+ @EpoxyAttribute
+ var selectedFilter: HomeRoomFilter? = null
+
override fun bind(holder: Holder) {
super.bind(holder)
with(holder.tabLayout) {
removeAllTabs()
+ clearOnTabSelectedListeners()
filtersData?.forEach { filter ->
- addTab(newTab().setText(filter.titleRes).setTag(filter))
+ addTab(
+ newTab().setText(filter.titleRes).setTag(filter),
+ filter == (selectedFilter ?: HomeRoomFilter.ALL)
+ )
}
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomsHeadersData.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomsHeadersData.kt
new file mode 100644
index 0000000000..87e45c99fa
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomsHeadersData.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.home.room.list.home.header
+
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+
+data class RoomsHeadersData(
+ val invitesCount: Int = 0,
+ val filtersList: List? = null,
+ val currentFilter: HomeRoomFilter? = null,
+ val recents: List? = null
+)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesCounterController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesCounterController.kt
deleted file mode 100644
index 82a31d30a9..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesCounterController.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * 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 im.vector.app.features.home.room.list.home.invites
-
-import com.airbnb.epoxy.EpoxyController
-import im.vector.app.core.resources.StringProvider
-import javax.inject.Inject
-
-class InvitesCounterController @Inject constructor(
- val stringProvider: StringProvider
-) : EpoxyController() {
-
- private var count = 0
- var clickListener: (() -> Unit)? = null
-
- override fun buildModels() {
- val host = this
- if (count != 0) {
- inviteCounterItem {
- id("invites_counter")
- invitesCount(host.count)
- listener { host.clickListener?.invoke() }
- }
- }
- }
-
- fun submitData(count: Int?) {
- this.count = count ?: 0
- requestModelBuild()
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt
deleted file mode 100644
index df5ce28da5..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/recent/RecentRoomCarouselController.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * 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 im.vector.app.features.home.room.list.home.recent
-
-import android.content.res.Resources
-import android.util.TypedValue
-import com.airbnb.epoxy.Carousel
-import com.airbnb.epoxy.CarouselModelBuilder
-import com.airbnb.epoxy.EpoxyController
-import com.airbnb.epoxy.EpoxyModel
-import com.airbnb.epoxy.carousel
-import com.google.android.material.color.MaterialColors
-import im.vector.app.R
-import im.vector.app.features.home.AvatarRenderer
-import im.vector.app.features.home.room.list.RoomListListener
-import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import org.matrix.android.sdk.api.util.toMatrixItem
-import javax.inject.Inject
-
-class RecentRoomCarouselController @Inject constructor(
- private val avatarRenderer: AvatarRenderer,
- private val resources: Resources,
-) : EpoxyController() {
-
- private var data: List? = null
- var listener: RoomListListener? = null
-
- private val hPadding = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- 4f,
- resources.displayMetrics
- ).toInt()
-
- private val topPadding = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- 12f,
- resources.displayMetrics
- ).toInt()
-
- fun submitList(recentList: List) {
- this.data = recentList
- requestModelBuild()
- }
-
- override fun buildModels() {
- val host = this
- data?.let { data ->
- carousel {
- id("recents_carousel")
- padding(Carousel.Padding(
- host.hPadding,
- host.topPadding,
- host.hPadding,
- 0,
- 0,
- )
- )
- onBind { _, view, _ ->
- val colorSurface = MaterialColors.getColor(view, R.attr.vctr_toolbar_background)
- view.setBackgroundColor(colorSurface)
- }
- withModelsFrom(data) { roomSummary ->
- val onClick = host.listener?.let { it::onRoomClicked }
- val onLongClick = host.listener?.let { it::onRoomLongClicked }
-
- RecentRoomItem_()
- .id(roomSummary.roomId)
- .avatarRenderer(host.avatarRenderer)
- .matrixItem(roomSummary.toMatrixItem())
- .unreadNotificationCount(roomSummary.notificationCount)
- .showHighlighted(roomSummary.highlightCount > 0)
- .itemLongClickListener { _ -> onLongClick?.invoke(roomSummary) ?: false }
- .itemClickListener { onClick?.invoke(roomSummary) }
- }
- }
- }
- }
-}
-
-private inline fun CarouselModelBuilder.withModelsFrom(
- items: List,
- modelBuilder: (T) -> EpoxyModel<*>
-) {
- models(items.map { modelBuilder(it) })
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt
index cefe107905..3320bdf314 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt
@@ -34,7 +34,7 @@ class ReleaseNotesPreferencesStore @Inject constructor(
private val context: Context
) {
- private val isAppLayoutOnboardingShown = booleanPreferencesKey("SETTINGS_APP_LAYOUT_ONBOARDING_SHOWN")
+ private val isAppLayoutOnboardingShown = booleanPreferencesKey("SETTINGS_APP_LAYOUT_ONBOARDING_DISPLAYED")
val appLayoutOnboardingShown: Flow = context.dataStore.data
.map { preferences -> preferences[isAppLayoutOnboardingShown].orFalse() }
diff --git a/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
index 6848bbb1f3..c2ecbe04b3 100644
--- a/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
+++ b/vector/src/main/java/im/vector/app/features/popup/VerificationVectorAlert.kt
@@ -38,13 +38,13 @@ class VerificationVectorAlert(
override val layoutRes = R.layout.alerter_verification_layout
class ViewBinder(
- private val matrixItem: MatrixItem?,
+ private val matrixItem: MatrixItem,
private val avatarRenderer: AvatarRenderer
) : VectorAlert.ViewBinder {
override fun bind(view: View) {
val views = AlerterVerificationLayoutBinding.bind(view)
- matrixItem?.let { avatarRenderer.render(it, views.ivUserAvatar, GlideApp.with(view.context.applicationContext)) }
+ avatarRenderer.render(matrixItem, views.ivUserAvatar, GlideApp.with(view.context.applicationContext))
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/FontScalePreferences.kt b/vector/src/main/java/im/vector/app/features/settings/FontScalePreferences.kt
index 292d0107ba..34862adc4f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/FontScalePreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/FontScalePreferences.kt
@@ -57,6 +57,16 @@ interface FontScalePreferences {
* @return list of values
*/
fun getAvailableScales(): List
+
+ companion object {
+ const val SCALE_TINY = 0.70f
+ const val SCALE_SMALL = 0.85f
+ const val SCALE_NORMAL = 1.00f
+ const val SCALE_LARGE = 1.15f
+ const val SCALE_LARGER = 1.30f
+ const val SCALE_LARGEST = 1.45f
+ const val SCALE_HUGE = 1.60f
+ }
}
/**
@@ -73,13 +83,13 @@ class FontScalePreferencesImpl @Inject constructor(
}
private val fontScaleValues = listOf(
- FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
- FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
- FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
- FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
- FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
- FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
- FontScaleValue(6, "FONT_SCALE_HUGE", 1.60f, R.string.huge)
+ FontScaleValue(0, "FONT_SCALE_TINY", FontScalePreferences.SCALE_TINY, R.string.tiny),
+ FontScaleValue(1, "FONT_SCALE_SMALL", FontScalePreferences.SCALE_SMALL, R.string.small),
+ FontScaleValue(2, "FONT_SCALE_NORMAL", FontScalePreferences.SCALE_NORMAL, R.string.normal),
+ FontScaleValue(3, "FONT_SCALE_LARGE", FontScalePreferences.SCALE_LARGE, R.string.large),
+ FontScaleValue(4, "FONT_SCALE_LARGER", FontScalePreferences.SCALE_LARGER, R.string.larger),
+ FontScaleValue(5, "FONT_SCALE_LARGEST", FontScalePreferences.SCALE_LARGEST, R.string.largest),
+ FontScaleValue(6, "FONT_SCALE_HUGE", FontScalePreferences.SCALE_HUGE, R.string.huge)
)
private val normalFontScaleValue = fontScaleValues[2]
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
index fca931eaef..4da6455f74 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
@@ -66,6 +66,7 @@ class VectorPreferences @Inject constructor(
const val SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY"
const val SETTINGS_LABS_PREFERENCE_KEY = "SETTINGS_LABS_PREFERENCE_KEY"
const val SETTINGS_LABS_NEW_APP_LAYOUT_KEY = "SETTINGS_LABS_NEW_APP_LAYOUT_KEY"
+ const val SETTINGS_LABS_DEFERRED_DM_KEY = "SETTINGS_LABS_DEFERRED_DM_KEY"
const val SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY"
const val SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY"
const val SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY"
@@ -1162,6 +1163,13 @@ class VectorPreferences @Inject constructor(
defaultPrefs.getBoolean(SETTINGS_LABS_NEW_APP_LAYOUT_KEY, getDefault(R.bool.settings_labs_new_app_layout_default))
}
+ /**
+ * Indicates whether or not deferred DMs are enabled.
+ */
+ fun isDeferredDmEnabled(): Boolean {
+ return defaultPrefs.getBoolean(SETTINGS_LABS_DEFERRED_DM_KEY, getDefault(R.bool.settings_labs_deferred_dm_default))
+ }
+
fun showLiveSenderInfo(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO, getDefault(R.bool.settings_timeline_show_live_sender_info_default))
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt
index e0b6368fc1..9c1b70a7e2 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt
@@ -24,26 +24,20 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
-import im.vector.app.core.platform.VectorViewModel
-import im.vector.app.core.utils.PublishDataSource
-import im.vector.lib.core.utils.flow.throttleFirst
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
-import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
-import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
-import kotlin.time.Duration.Companion.seconds
class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState,
- private val activeSessionHolder: ActiveSessionHolder,
+ activeSessionHolder: ActiveSessionHolder,
private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
- private val refreshDevicesUseCase: RefreshDevicesUseCase,
private val refreshDevicesOnCryptoDevicesChangeUseCase: RefreshDevicesOnCryptoDevicesChangeUseCase,
-) : VectorViewModel(initialState), VerificationService.Listener {
+ refreshDevicesUseCase: RefreshDevicesUseCase,
+) : VectorSessionsListViewModel(initialState, activeSessionHolder, refreshDevicesUseCase) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory {
@@ -52,35 +46,11 @@ class DevicesViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
- private val refreshSource = PublishDataSource()
- private val refreshThrottleDelayMs = 4.seconds.inWholeMilliseconds
-
init {
- addVerificationListener()
observeCurrentSessionCrossSigningInfo()
observeDevices()
- observeRefreshSource()
refreshDevicesOnCryptoDevicesChange()
- queryRefreshDevicesList()
- }
-
- override fun onCleared() {
- removeVerificationListener()
- super.onCleared()
- }
-
- private fun addVerificationListener() {
- activeSessionHolder.getSafeActiveSession()
- ?.cryptoService()
- ?.verificationService()
- ?.addListener(this)
- }
-
- private fun removeVerificationListener() {
- activeSessionHolder.getSafeActiveSession()
- ?.cryptoService()
- ?.verificationService()
- ?.removeListener(this)
+ refreshDeviceList()
}
private fun observeCurrentSessionCrossSigningInfo() {
@@ -94,7 +64,10 @@ class DevicesViewModel @AssistedInject constructor(
}
private fun observeDevices() {
- getDeviceFullInfoListUseCase.execute()
+ getDeviceFullInfoListUseCase.execute(
+ filterType = DeviceManagerFilterType.ALL_SESSIONS,
+ excludeCurrentDevice = false
+ )
.execute { async ->
if (async is Success) {
val deviceFullInfoList = async.invoke()
@@ -119,28 +92,6 @@ class DevicesViewModel @AssistedInject constructor(
}
}
- private fun observeRefreshSource() {
- refreshSource.stream()
- .throttleFirst(refreshThrottleDelayMs)
- .onEach { refreshDevicesUseCase.execute() }
- .launchIn(viewModelScope)
- }
-
- override fun transactionUpdated(tx: VerificationTransaction) {
- if (tx.state == VerificationTxState.Verified) {
- queryRefreshDevicesList()
- }
- }
-
- /**
- * Force the refresh of the devices list.
- * The devices list is the list of the devices where the user is logged in.
- * It can be any mobile devices, and any browsers.
- */
- private fun queryRefreshDevicesList() {
- refreshSource.post(Unit)
- }
-
override fun handle(action: DevicesAction) {
when (action) {
is DevicesAction.MarkAsManuallyVerified -> handleMarkAsManuallyVerifiedAction()
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
index da2cf25f39..3c0d3a5e56 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
@@ -17,6 +17,8 @@
package im.vector.app.features.settings.devices.v2
import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -32,16 +34,23 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase,
private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase,
private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
+ private val filterDevicesUseCase: FilterDevicesUseCase,
) {
- fun execute(): Flow> {
+ fun execute(filterType: DeviceManagerFilterType, excludeCurrentDevice: Boolean = false): Flow> {
return activeSessionHolder.getSafeActiveSession()?.let { session ->
val deviceFullInfoFlow = combine(
getCurrentSessionCrossSigningInfoUseCase.execute(),
session.flow().liveUserCryptoDevices(session.myUserId),
session.flow().liveMyDevicesInfo()
) { currentSessionCrossSigningInfo, cryptoList, infoList ->
- convertToDeviceFullInfoList(currentSessionCrossSigningInfo, cryptoList, infoList)
+ val deviceFullInfoList = convertToDeviceFullInfoList(currentSessionCrossSigningInfo, cryptoList, infoList)
+ val excludedDeviceIds = if (excludeCurrentDevice) {
+ listOf(currentSessionCrossSigningInfo.deviceId)
+ } else {
+ emptyList()
+ }
+ filterDevicesUseCase.execute(deviceFullInfoList, filterType, excludedDeviceIds)
}
deviceFullInfoFlow.distinctUntilChanged()
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSessionsListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSessionsListViewModel.kt
new file mode 100644
index 0000000000..8cb69a31ed
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSessionsListViewModel.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2
+
+import com.airbnb.mvrx.MavericksState
+import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.platform.VectorViewEvents
+import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.platform.VectorViewModelAction
+import im.vector.app.core.utils.PublishDataSource
+import im.vector.lib.core.utils.flow.throttleFirst
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
+import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
+import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
+import kotlin.time.Duration.Companion.seconds
+
+abstract class VectorSessionsListViewModel(
+ initialState: S,
+ private val activeSessionHolder: ActiveSessionHolder,
+ private val refreshDevicesUseCase: RefreshDevicesUseCase,
+) : VectorViewModel(initialState), VerificationService.Listener {
+
+ private val refreshSource = PublishDataSource()
+ private val refreshThrottleDelayMs = 4.seconds.inWholeMilliseconds
+
+ init {
+ addVerificationListener()
+ observeRefreshSource()
+ }
+
+ override fun onCleared() {
+ removeVerificationListener()
+ super.onCleared()
+ }
+
+ private fun addVerificationListener() {
+ activeSessionHolder.getSafeActiveSession()
+ ?.cryptoService()
+ ?.verificationService()
+ ?.addListener(this)
+ }
+
+ private fun removeVerificationListener() {
+ activeSessionHolder.getSafeActiveSession()
+ ?.cryptoService()
+ ?.verificationService()
+ ?.removeListener(this)
+ }
+
+ private fun observeRefreshSource() {
+ refreshSource.stream()
+ .throttleFirst(refreshThrottleDelayMs)
+ .onEach { refreshDevicesUseCase.execute() }
+ .launchIn(viewModelScope)
+ }
+
+ override fun transactionUpdated(tx: VerificationTransaction) {
+ if (tx.state == VerificationTxState.Verified) {
+ refreshDeviceList()
+ }
+ }
+
+ /**
+ * Force the refresh of the devices list.
+ * The devices list is the list of the devices where the user is logged in.
+ * It can be any mobile devices, and any browsers.
+ */
+ fun refreshDeviceList() {
+ refreshSource.post(Unit)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index acf33dc01d..1e91384c3a 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -37,7 +37,8 @@ import im.vector.app.core.resources.DrawableProvider
import im.vector.app.databinding.FragmentSettingsDevicesBinding
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.VerificationBottomSheet
-import im.vector.app.features.settings.devices.v2.list.OtherSessionsController
+import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
+import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
@@ -48,7 +49,8 @@ import javax.inject.Inject
*/
@AndroidEntryPoint
class VectorSettingsDevicesFragment :
- VectorBaseFragment() {
+ VectorBaseFragment(),
+ OtherSessionsView.Callback {
@Inject lateinit var viewNavigator: VectorSettingsDevicesViewNavigator
@@ -120,11 +122,7 @@ class VectorSettingsDevicesFragment :
}
private fun initOtherSessionsView() {
- views.deviceListOtherSessions.setCallback(object : OtherSessionsController.Callback {
- override fun onItemClicked(deviceId: String) {
- navigateToSessionOverview(deviceId)
- }
- })
+ views.deviceListOtherSessions.callback = this
}
override fun onDestroyView() {
@@ -201,7 +199,11 @@ class VectorSettingsDevicesFragment :
} else {
views.deviceListHeaderOtherSessions.isVisible = true
views.deviceListOtherSessions.isVisible = true
- views.deviceListOtherSessions.render(otherDevices)
+ views.deviceListOtherSessions.render(
+ devices = otherDevices.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER),
+ totalNumberOfDevices = otherDevices.size,
+ showViewAll = otherDevices.size > NUMBER_OF_OTHER_DEVICES_TO_RENDER
+ )
}
}
@@ -252,4 +254,12 @@ class VectorSettingsDevicesFragment :
private fun handleLoadingStatus(isLoading: Boolean) {
views.waitingView.root.isVisible = isLoading
}
+
+ override fun onOtherSessionClicked(deviceId: String) {
+ navigateToSessionOverview(deviceId)
+ }
+
+ override fun onViewAllOtherSessionsClicked() {
+ viewNavigator.navigateToOtherSessions(requireActivity())
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt
index 54eed3bc14..486785c918 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt
@@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2
import android.content.Context
+import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
import javax.inject.Inject
@@ -25,4 +26,8 @@ class VectorSettingsDevicesViewNavigator @Inject constructor() {
fun navigateToSessionOverview(context: Context, deviceId: String) {
context.startActivity(SessionOverviewActivity.newIntent(context, deviceId))
}
+
+ fun navigateToOtherSessions(context: Context) {
+ context.startActivity(OtherSessionsActivity.newIntent(context))
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt
new file mode 100644
index 0000000000..25b5ddb0e8
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import javax.inject.Inject
+
+class CheckIfSectionDeviceIsVisibleUseCase @Inject constructor() {
+
+ fun execute(deviceInfo: DeviceInfo): Boolean {
+ return deviceInfo.lastSeenIp?.isNotEmpty().orFalse()
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCase.kt
new file mode 100644
index 0000000000..4998b4b5d3
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCase.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import javax.inject.Inject
+
+class CheckIfSectionSessionIsVisibleUseCase @Inject constructor() {
+
+ fun execute(deviceInfo: DeviceInfo): Boolean {
+ return deviceInfo.displayName?.isNotEmpty().orFalse() ||
+ deviceInfo.deviceId?.isNotEmpty().orFalse() ||
+ (deviceInfo.lastSeenTs ?: 0) > 0
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsAction.kt
new file mode 100644
index 0000000000..0fa524dab4
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsAction.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class SessionDetailsAction : VectorViewModelAction {
+ data class CopyToClipboard(val content: String) : SessionDetailsAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt
new file mode 100644
index 0000000000..101bf1da2e
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import com.airbnb.mvrx.Mavericks
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.platform.SimpleFragmentActivity
+
+/**
+ * Display the details info about a Session.
+ */
+@AndroidEntryPoint
+class SessionDetailsActivity : SimpleFragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (isFirstCreation()) {
+ addFragment(
+ container = views.container,
+ fragmentClass = SessionDetailsFragment::class.java,
+ params = intent.getParcelableExtra(Mavericks.KEY_ARG)
+ )
+ }
+ }
+
+ companion object {
+ fun newIntent(context: Context, deviceId: String): Intent {
+ return Intent(context, SessionDetailsActivity::class.java).apply {
+ putExtra(Mavericks.KEY_ARG, SessionDetailsArgs(deviceId))
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsArgs.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsArgs.kt
new file mode 100644
index 0000000000..97716d7c47
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsArgs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class SessionDetailsArgs(
+ val deviceId: String
+) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsContentItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsContentItem.kt
new file mode 100644
index 0000000000..665ba5126c
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsContentItem.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.view.View
+import android.widget.TextView
+import androidx.core.view.isVisible
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
+
+@EpoxyModelClass
+abstract class SessionDetailsContentItem : VectorEpoxyModel(R.layout.item_session_details_content) {
+
+ @EpoxyAttribute
+ var title: String? = null
+
+ @EpoxyAttribute
+ var description: String? = null
+
+ @EpoxyAttribute
+ var hasDivider: Boolean = true
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ var onLongClickListener: View.OnLongClickListener? = null
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.sessionDetailsContentTitle.text = title
+ holder.sessionDetailsContentDescription.text = description
+ holder.view.isClickable = onLongClickListener != null
+ holder.view.setOnLongClickListener(onLongClickListener)
+ holder.sessionDetailsContentDivider.isVisible = hasDivider
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val sessionDetailsContentTitle by bind(R.id.sessionDetailsContentTitle)
+ val sessionDetailsContentDescription by bind(R.id.sessionDetailsContentDescription)
+ val sessionDetailsContentDivider by bind(R.id.sessionDetailsContentDivider)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt
new file mode 100644
index 0000000000..1fb5be4d78
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.view.View
+import androidx.annotation.StringRes
+import com.airbnb.epoxy.TypedEpoxyController
+import im.vector.app.R
+import im.vector.app.core.date.DateFormatKind
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.core.utils.DimensionConverter
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import javax.inject.Inject
+
+class SessionDetailsController @Inject constructor(
+ private val checkIfSectionSessionIsVisibleUseCase: CheckIfSectionSessionIsVisibleUseCase,
+ private val checkIfSectionDeviceIsVisibleUseCase: CheckIfSectionDeviceIsVisibleUseCase,
+ private val stringProvider: StringProvider,
+ private val dateFormatter: VectorDateFormatter,
+ private val dimensionConverter: DimensionConverter,
+) : TypedEpoxyController() {
+
+ var callback: Callback? = null
+
+ interface Callback {
+ fun onItemLongClicked(content: String)
+ }
+
+ override fun buildModels(data: DeviceInfo?) {
+ data?.let { info ->
+ val hasSectionSession = hasSectionSession(data)
+ if (hasSectionSession) {
+ buildSectionSession(info)
+ }
+
+ if (hasSectionDevice(data)) {
+ buildSectionDevice(info, addExtraTopMargin = hasSectionSession)
+ }
+ }
+ }
+
+ private fun buildHeaderItem(@StringRes titleResId: Int, addExtraTopMargin: Boolean = false) {
+ val host = this
+ sessionDetailsHeaderItem {
+ id(titleResId)
+ title(host.stringProvider.getString(titleResId))
+ addExtraTopMargin(addExtraTopMargin)
+ dimensionConverter(host.dimensionConverter)
+ }
+ }
+
+ private fun buildContentItem(@StringRes titleResId: Int, value: String, hasDivider: Boolean) {
+ val host = this
+ sessionDetailsContentItem {
+ id(titleResId)
+ title(host.stringProvider.getString(titleResId))
+ description(value)
+ hasDivider(hasDivider)
+ onLongClickListener(View.OnLongClickListener {
+ host.callback?.onItemLongClicked(value)
+ true
+ })
+ }
+ }
+
+ private fun hasSectionSession(data: DeviceInfo): Boolean {
+ return checkIfSectionSessionIsVisibleUseCase.execute(data)
+ }
+
+ private fun buildSectionSession(data: DeviceInfo) {
+ val sessionName = data.displayName
+ val sessionId = data.deviceId
+ val sessionLastSeenTs = data.lastSeenTs
+
+ buildHeaderItem(R.string.device_manager_session_title)
+
+ sessionName?.let {
+ val hasDivider = sessionId != null || sessionLastSeenTs != null
+ buildContentItem(R.string.device_manager_session_details_session_name, it, hasDivider)
+ }
+ sessionId?.let {
+ val hasDivider = sessionLastSeenTs != null
+ buildContentItem(R.string.device_manager_session_details_session_id, it, hasDivider)
+ }
+ sessionLastSeenTs?.let {
+ val formattedDate = dateFormatter.format(it, DateFormatKind.MESSAGE_DETAIL)
+ val hasDivider = false
+ buildContentItem(R.string.device_manager_session_details_session_last_activity, formattedDate, hasDivider)
+ }
+ }
+
+ private fun hasSectionDevice(data: DeviceInfo): Boolean {
+ return checkIfSectionDeviceIsVisibleUseCase.execute(data)
+ }
+
+ private fun buildSectionDevice(data: DeviceInfo, addExtraTopMargin: Boolean) {
+ val lastSeenIp = data.lastSeenIp
+
+ buildHeaderItem(R.string.device_manager_device_title, addExtraTopMargin)
+
+ lastSeenIp?.let {
+ val hasDivider = false
+ buildContentItem(R.string.device_manager_session_details_device_ip_address, it, hasDivider)
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt
new file mode 100644
index 0000000000..5d7717e5f7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.extensions.cleanup
+import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.core.platform.showOptimizedSnackbar
+import im.vector.app.databinding.FragmentSessionDetailsBinding
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import javax.inject.Inject
+
+/**
+ * Display the details info about a Session.
+ */
+@AndroidEntryPoint
+class SessionDetailsFragment :
+ VectorBaseFragment() {
+
+ @Inject lateinit var sessionDetailsController: SessionDetailsController
+
+ private val viewModel: SessionDetailsViewModel by fragmentViewModel()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSessionDetailsBinding {
+ return FragmentSessionDetailsBinding.inflate(inflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initToolbar()
+ initSessionDetails()
+ observeViewEvents()
+ }
+
+ private fun initToolbar() {
+ (activity as? AppCompatActivity)
+ ?.supportActionBar
+ ?.setTitle(R.string.device_manager_session_details_title)
+ }
+
+ private fun initSessionDetails() {
+ sessionDetailsController.callback = object : SessionDetailsController.Callback {
+ override fun onItemLongClicked(content: String) {
+ viewModel.handle(SessionDetailsAction.CopyToClipboard(content))
+ }
+ }
+ views.sessionDetails.configureWith(sessionDetailsController)
+ }
+
+ private fun observeViewEvents() {
+ viewModel.observeViewEvents { viewEvent ->
+ when (viewEvent) {
+ SessionDetailsViewEvent.ContentCopiedToClipboard -> view?.showOptimizedSnackbar(getString(R.string.copied_to_clipboard))
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ cleanUpSessionDetails()
+ super.onDestroyView()
+ }
+
+ private fun cleanUpSessionDetails() {
+ sessionDetailsController.callback = null
+ views.sessionDetails.cleanup()
+ }
+
+ override fun invalidate() = withState(viewModel) { state ->
+ if (state.deviceInfo is Success) {
+ renderSessionDetails(state.deviceInfo.invoke())
+ } else {
+ hideSessionDetails()
+ }
+ }
+
+ private fun renderSessionDetails(deviceInfo: DeviceInfo) {
+ views.sessionDetails.isVisible = true
+ sessionDetailsController.setData(deviceInfo)
+ }
+
+ private fun hideSessionDetails() {
+ views.sessionDetails.isGone = true
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsHeaderItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsHeaderItem.kt
new file mode 100644
index 0000000000..ff6ce3faad
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsHeaderItem.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.core.view.updateLayoutParams
+import androidx.core.view.updateMargins
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.app.core.utils.DimensionConverter
+
+private const val EXTRA_TOP_MARGIN_DP = 48
+
+@EpoxyModelClass
+abstract class SessionDetailsHeaderItem : VectorEpoxyModel(R.layout.item_session_details_header) {
+
+ @EpoxyAttribute
+ var title: String? = null
+
+ @EpoxyAttribute
+ var addExtraTopMargin: Boolean = false
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ var dimensionConverter: DimensionConverter? = null
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.sessionDetailsHeaderTitle.text = title
+ val topMargin = if (addExtraTopMargin) {
+ dimensionConverter?.dpToPx(EXTRA_TOP_MARGIN_DP) ?: 0
+ } else {
+ 0
+ }
+ holder.sessionDetailsHeaderTitle.updateLayoutParams {
+ updateMargins(top = topMargin)
+ }
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val sessionDetailsHeaderTitle by bind(R.id.sessionDetailsHeaderTitle)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewEvent.kt
new file mode 100644
index 0000000000..02b313319e
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewEvent.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import im.vector.app.core.platform.VectorViewEvents
+
+sealed class SessionDetailsViewEvent : VectorViewEvents {
+ object ContentCopiedToClipboard : SessionDetailsViewEvent()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt
new file mode 100644
index 0000000000..c37858cc54
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import com.airbnb.mvrx.MavericksViewModelFactory
+import com.airbnb.mvrx.Success
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.utils.CopyToClipboardUseCase
+import im.vector.app.features.settings.devices.v2.overview.GetDeviceFullInfoUseCase
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+class SessionDetailsViewModel @AssistedInject constructor(
+ @Assisted val initialState: SessionDetailsViewState,
+ private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
+ private val copyToClipboardUseCase: CopyToClipboardUseCase,
+) : VectorViewModel(initialState) {
+
+ companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
+
+ @AssistedFactory
+ interface Factory : MavericksAssistedViewModelFactory {
+ override fun create(initialState: SessionDetailsViewState): SessionDetailsViewModel
+ }
+
+ init {
+ observeSessionInfo(initialState.deviceId)
+ }
+
+ private fun observeSessionInfo(deviceId: String) {
+ getDeviceFullInfoUseCase.execute(deviceId)
+ .onEach { setState { copy(deviceInfo = Success(it.deviceInfo)) } }
+ .launchIn(viewModelScope)
+ }
+
+ override fun handle(action: SessionDetailsAction) {
+ return when (action) {
+ is SessionDetailsAction.CopyToClipboard -> handleCopyToClipboard(action)
+ }
+ }
+
+ private fun handleCopyToClipboard(copyToClipboard: SessionDetailsAction.CopyToClipboard) {
+ copyToClipboardUseCase.execute(copyToClipboard.content)
+ _viewEvents.post(SessionDetailsViewEvent.ContentCopiedToClipboard)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt
new file mode 100644
index 0000000000..15868d3110
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MavericksState
+import com.airbnb.mvrx.Uninitialized
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+
+data class SessionDetailsViewState(
+ val deviceId: String,
+ val deviceInfo: Async = Uninitialized,
+) : MavericksState {
+ constructor(args: SessionDetailsArgs) : this(
+ deviceId = args.deviceId
+ )
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterBottomSheet.kt
new file mode 100644
index 0000000000..28c7045a82
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterBottomSheet.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.filter
+
+import android.os.Bundle
+import android.os.Parcelable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.args
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment.ResultListener.Companion.RESULT_OK
+import im.vector.app.databinding.BottomSheetDeviceManagerFilterBinding
+import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class DeviceManagerFilterBottomSheetArgs(
+ val initialFilterType: DeviceManagerFilterType,
+) : Parcelable
+
+@AndroidEntryPoint
+class DeviceManagerFilterBottomSheet : VectorBaseBottomSheetDialogFragment() {
+
+ private val args: DeviceManagerFilterBottomSheetArgs by args()
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetDeviceManagerFilterBinding {
+ return BottomSheetDeviceManagerFilterBinding.inflate(inflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initFilterRadioGroup()
+ }
+
+ private fun initFilterRadioGroup() {
+ views.filterOptionInactiveTextView.text = resources.getQuantityString(
+ R.plurals.device_manager_filter_option_inactive_description,
+ SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
+ SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
+ )
+
+ val radioButtonId = when (args.initialFilterType) {
+ DeviceManagerFilterType.ALL_SESSIONS -> R.id.filterOptionAllSessionsRadioButton
+ DeviceManagerFilterType.VERIFIED -> R.id.filterOptionVerifiedRadioButton
+ DeviceManagerFilterType.UNVERIFIED -> R.id.filterOptionUnverifiedRadioButton
+ DeviceManagerFilterType.INACTIVE -> R.id.filterOptionInactiveRadioButton
+ }
+ views.filterOptionsRadioGroup.check(radioButtonId)
+
+ views.filterOptionVerifiedTextView.debouncedClicks {
+ views.filterOptionsRadioGroup.check(R.id.filterOptionVerifiedRadioButton)
+ }
+ views.filterOptionUnverifiedTextView.debouncedClicks {
+ views.filterOptionsRadioGroup.check(R.id.filterOptionUnverifiedRadioButton)
+ }
+ views.filterOptionInactiveTextView.debouncedClicks {
+ views.filterOptionsRadioGroup.check(R.id.filterOptionInactiveRadioButton)
+ }
+
+ views.filterOptionsRadioGroup.setOnCheckedChangeListener { _, checkedId ->
+ onFilterTypeChanged(checkedId)
+ }
+ }
+
+ private fun onFilterTypeChanged(checkedId: Int) {
+ val filterType = when (checkedId) {
+ R.id.filterOptionAllSessionsRadioButton -> DeviceManagerFilterType.ALL_SESSIONS
+ R.id.filterOptionVerifiedRadioButton -> DeviceManagerFilterType.VERIFIED
+ R.id.filterOptionUnverifiedRadioButton -> DeviceManagerFilterType.UNVERIFIED
+ R.id.filterOptionInactiveRadioButton -> DeviceManagerFilterType.INACTIVE
+ else -> DeviceManagerFilterType.ALL_SESSIONS
+ }
+ resultListener?.onBottomSheetResult(RESULT_OK, filterType)
+ dismiss()
+ }
+
+ companion object {
+ fun newInstance(initialFilterType: DeviceManagerFilterType, resultListener: ResultListener): DeviceManagerFilterBottomSheet {
+ return DeviceManagerFilterBottomSheet().apply {
+ this.resultListener = resultListener
+ setArguments(DeviceManagerFilterBottomSheetArgs(initialFilterType))
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterType.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterType.kt
new file mode 100644
index 0000000000..a1ef08f7df
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/DeviceManagerFilterType.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.filter
+
+enum class DeviceManagerFilterType {
+ ALL_SESSIONS,
+ VERIFIED,
+ UNVERIFIED,
+ INACTIVE,
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt
new file mode 100644
index 0000000000..e0bb567dc6
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.filter
+
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import org.matrix.android.sdk.api.extensions.orFalse
+import javax.inject.Inject
+
+class FilterDevicesUseCase @Inject constructor() {
+
+ fun execute(
+ devices: List,
+ filterType: DeviceManagerFilterType,
+ excludedDeviceIds: List = emptyList(),
+ ): List {
+ return devices
+ .filter {
+ when (filterType) {
+ DeviceManagerFilterType.ALL_SESSIONS -> true
+ DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.isVerified.orFalse()
+ DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.isVerified.orFalse()
+ DeviceManagerFilterType.INACTIVE -> it.isInactive
+ }
+ }
+ .filter { it.deviceInfo.deviceId !in excludedDeviceIds }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
index 468b19c45a..06f3373f61 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
@@ -50,7 +50,7 @@ class OtherSessionsController @Inject constructor(
text(host.stringProvider.getString(R.string.no_result_placeholder))
}
} else {
- data.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER).forEach { device ->
+ data.forEach { device ->
val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
val description = if (device.isInactive) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt
index b552664fe9..6f6956c885 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt
@@ -19,8 +19,13 @@ package im.vector.app.features.settings.devices.v2.list
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.airbnb.epoxy.OnModelBuildFinishedListener
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
+import im.vector.app.core.epoxy.LayoutManagerStateRestorer
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.databinding.ViewOtherSessionsBinding
@@ -32,30 +37,74 @@ class OtherSessionsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
-) : ConstraintLayout(context, attrs, defStyleAttr) {
+) : ConstraintLayout(context, attrs, defStyleAttr), OtherSessionsController.Callback {
+
+ interface Callback {
+ fun onOtherSessionClicked(deviceId: String)
+ fun onViewAllOtherSessionsClicked()
+ }
@Inject lateinit var otherSessionsController: OtherSessionsController
private val views: ViewOtherSessionsBinding
+ private lateinit var recyclerViewDataObserver: RecyclerView.AdapterDataObserver
+ private lateinit var stateRestorer: LayoutManagerStateRestorer
+ private var modelBuildListener: OnModelBuildFinishedListener? = null
+
+ var callback: Callback? = null
init {
inflate(context, R.layout.view_other_sessions, this)
views = ViewOtherSessionsBinding.bind(this)
+
+ configureOtherSessionsRecyclerView()
+
+ views.otherSessionsViewAllButton.setOnClickListener {
+ callback?.onViewAllOtherSessionsClicked()
+ }
}
- fun render(devices: List) {
- views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true)
- views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size)
+ private fun configureOtherSessionsRecyclerView() {
+ views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = false)
+
+ val layoutManager = LinearLayoutManager(context)
+ stateRestorer = LayoutManagerStateRestorer(layoutManager)
+ views.otherSessionsRecyclerView.layoutManager = layoutManager
+ layoutManager.recycleChildrenOnDetach = true
+ modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
+ otherSessionsController.addModelBuildListener(modelBuildListener)
+
+ recyclerViewDataObserver = object : RecyclerView.AdapterDataObserver() {
+ override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+ super.onItemRangeInserted(positionStart, itemCount)
+ views.otherSessionsRecyclerView.scrollToPosition(0)
+ }
+ }
+ otherSessionsController.adapter.registerAdapterDataObserver(recyclerViewDataObserver)
+
+ otherSessionsController.callback = this
+ }
+
+ fun render(devices: List, totalNumberOfDevices: Int, showViewAll: Boolean) {
+ if (showViewAll) {
+ views.otherSessionsViewAllButton.isVisible = true
+ views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, totalNumberOfDevices)
+ } else {
+ views.otherSessionsViewAllButton.isVisible = false
+ }
otherSessionsController.setData(devices)
}
- fun setCallback(callback: OtherSessionsController.Callback) {
- otherSessionsController.callback = callback
- }
-
override fun onDetachedFromWindow() {
+ otherSessionsController.removeModelBuildListener(modelBuildListener)
+ modelBuildListener = null
otherSessionsController.callback = null
+ otherSessionsController.adapter.unregisterAdapterDataObserver(recyclerViewDataObserver)
views.otherSessionsRecyclerView.cleanup()
super.onDetachedFromWindow()
}
+
+ override fun onItemClicked(deviceId: String) {
+ callback?.onOtherSessionClicked(deviceId)
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
index 0cb621a502..555d216dfc 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
@@ -24,6 +24,7 @@ import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider
@@ -91,13 +92,14 @@ class SessionInfoView @JvmOverloads constructor(
private fun appendLearnMoreToVerificationStatus() {
val status = views.sessionInfoVerificationStatusDetailTextView.text
val learnMore = context.getString(R.string.action_learn_more)
- val stringBuilder = StringBuilder()
- stringBuilder.append(status)
- stringBuilder.append(" ")
- stringBuilder.append(learnMore)
+ val statusText = buildString {
+ append(status)
+ append(" ")
+ append(learnMore)
+ }
views.sessionInfoVerificationStatusDetailTextView.setTextWithColoredPart(
- fullText = stringBuilder.toString(),
+ fullText = statusText,
coloredPart = learnMore,
underline = false
) {
@@ -172,15 +174,7 @@ class SessionInfoView @JvmOverloads constructor(
views.sessionInfoLastActivityTextView.isGone = true
}
- deviceInfo.lastSeenIp
- ?.takeIf { isLastSeenDetailsVisible }
- ?.let { ipAddress ->
- views.sessionInfoLastIPAddressTextView.isVisible = true
- views.sessionInfoLastIPAddressTextView.text = ipAddress
- }
- ?: run {
- views.sessionInfoLastIPAddressTextView.isGone = true
- }
+ views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible })
}
private fun renderDetailsButton(isDetailsButtonVisible: Boolean) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
index 547ed93f24..ef8682df01 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
@@ -24,6 +24,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.use
import androidx.core.view.isVisible
import im.vector.app.R
+import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.databinding.ViewSessionsListHeaderBinding
@@ -53,26 +54,27 @@ class SessionsListHeaderView @JvmOverloads constructor(
}
private fun setTitle(typedArray: TypedArray) {
- val title = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderTitle)
- binding.sessionsListHeaderTitle.text = title
+ val title = typedArray.getString(R.styleable.SessionsListHeaderView_sessionsListHeaderTitle)
+ binding.sessionsListHeaderTitle.setTextOrHide(title)
}
private fun setDescription(typedArray: TypedArray) {
- val description = typedArray.getString(R.styleable.SessionsListHeaderView_devicesListHeaderDescription)
+ val description = typedArray.getString(R.styleable.SessionsListHeaderView_sessionsListHeaderDescription)
if (description.isNullOrEmpty()) {
binding.sessionsListHeaderDescription.isVisible = false
return
}
val learnMore = context.getString(R.string.action_learn_more)
- val stringBuilder = StringBuilder()
- stringBuilder.append(description)
- stringBuilder.append(" ")
- stringBuilder.append(learnMore)
+ val fullDescription = buildString {
+ append(description)
+ append(" ")
+ append(learnMore)
+ }
binding.sessionsListHeaderDescription.isVisible = true
binding.sessionsListHeaderDescription.setTextWithColoredPart(
- fullText = stringBuilder.toString(),
+ fullText = fullDescription,
coloredPart = learnMore,
underline = false
) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsAction.kt
new file mode 100644
index 0000000000..7164ecc866
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsAction.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import im.vector.app.core.platform.VectorViewModelAction
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+
+sealed class OtherSessionsAction : VectorViewModelAction {
+ data class FilterDevices(val filterType: DeviceManagerFilterType) : OtherSessionsAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt
new file mode 100644
index 0000000000..b9ab59d8f5
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.platform.SimpleFragmentActivity
+
+@AndroidEntryPoint
+class OtherSessionsActivity : SimpleFragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ views.toolbar.visibility = View.GONE
+
+ if (isFirstCreation()) {
+ addFragment(
+ container = views.container,
+ fragmentClass = OtherSessionsFragment::class.java
+ )
+ }
+ }
+
+ companion object {
+ fun newIntent(context: Context): Intent {
+ return Intent(context, OtherSessionsActivity::class.java)
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt
new file mode 100644
index 0000000000..81ea5f4b89
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment.ResultListener.Companion.RESULT_OK
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.databinding.FragmentOtherSessionsBinding
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterBottomSheet
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
+import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
+import im.vector.app.features.themes.ThemeUtils
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class OtherSessionsFragment :
+ VectorBaseFragment(),
+ VectorBaseBottomSheetDialogFragment.ResultListener,
+ OtherSessionsView.Callback {
+
+ private val viewModel: OtherSessionsViewModel by fragmentViewModel()
+
+ @Inject lateinit var colorProvider: ColorProvider
+
+ @Inject lateinit var viewNavigator: OtherSessionsViewNavigator
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentOtherSessionsBinding {
+ return FragmentOtherSessionsBinding.inflate(layoutInflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupToolbar(views.otherSessionsToolbar).allowBack()
+ observeViewEvents()
+ initFilterView()
+ }
+
+ private fun observeViewEvents() {
+ viewModel.observeViewEvents {
+ when (it) {
+ is OtherSessionsViewEvents.Loading -> showLoading(it.message)
+ is OtherSessionsViewEvents.Failure -> showFailure(it.throwable)
+ }
+ }
+ }
+
+ private fun initFilterView() {
+ views.otherSessionsFilterFrameLayout.debouncedClicks {
+ withState(viewModel) { state ->
+ DeviceManagerFilterBottomSheet
+ .newInstance(state.currentFilter, this)
+ .show(requireActivity().supportFragmentManager, "SHOW_DEVICE_MANAGER_FILTER_BOTTOM_SHEET")
+ }
+ }
+
+ views.otherSessionsClearFilterButton.debouncedClicks {
+ viewModel.handle(OtherSessionsAction.FilterDevices(DeviceManagerFilterType.ALL_SESSIONS))
+ }
+
+ views.deviceListOtherSessions.callback = this
+ }
+
+ override fun onBottomSheetResult(resultCode: Int, data: Any?) {
+ if (resultCode == RESULT_OK && data != null && data is DeviceManagerFilterType) {
+ viewModel.handle(OtherSessionsAction.FilterDevices(data))
+ }
+ }
+
+ override fun invalidate() = withState(viewModel) { state ->
+ if (state.devices is Success) {
+ renderDevices(state.devices(), state.currentFilter)
+ }
+ }
+
+ private fun renderDevices(devices: List?, currentFilter: DeviceManagerFilterType) {
+ views.otherSessionsFilterBadgeImageView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
+ views.otherSessionsSecurityRecommendationView.isVisible = currentFilter != DeviceManagerFilterType.ALL_SESSIONS
+ views.deviceListHeaderOtherSessions.isVisible = currentFilter == DeviceManagerFilterType.ALL_SESSIONS
+
+ when (currentFilter) {
+ DeviceManagerFilterType.VERIFIED -> {
+ views.otherSessionsSecurityRecommendationView.render(
+ OtherSessionsSecurityRecommendationViewState(
+ title = getString(R.string.device_manager_other_sessions_recommendation_title_verified),
+ description = getString(R.string.device_manager_other_sessions_recommendation_description_verified),
+ imageResourceId = R.drawable.ic_shield_trusted_no_border,
+ imageTintColorResourceId = colorProvider.getColor(R.color.shield_color_trust_background)
+ )
+ )
+ views.otherSessionsNotFoundTextView.text = getString(R.string.device_manager_other_sessions_no_verified_sessions_found)
+ }
+ DeviceManagerFilterType.UNVERIFIED -> {
+ views.otherSessionsSecurityRecommendationView.render(
+ OtherSessionsSecurityRecommendationViewState(
+ title = getString(R.string.device_manager_other_sessions_recommendation_title_unverified),
+ description = getString(R.string.device_manager_other_sessions_recommendation_description_unverified),
+ imageResourceId = R.drawable.ic_shield_warning_no_border,
+ imageTintColorResourceId = colorProvider.getColor(R.color.shield_color_warning_background)
+ )
+ )
+ views.otherSessionsNotFoundTextView.text = getString(R.string.device_manager_other_sessions_no_unverified_sessions_found)
+ }
+ DeviceManagerFilterType.INACTIVE -> {
+ views.otherSessionsSecurityRecommendationView.render(
+ OtherSessionsSecurityRecommendationViewState(
+ title = getString(R.string.device_manager_other_sessions_recommendation_title_inactive),
+ description = resources.getQuantityString(
+ R.plurals.device_manager_other_sessions_recommendation_description_inactive,
+ SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
+ SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
+ ),
+ imageResourceId = R.drawable.ic_inactive_sessions,
+ imageTintColorResourceId = ThemeUtils.getColor(requireContext(), R.attr.vctr_system)
+ )
+ )
+ views.otherSessionsNotFoundTextView.text = getString(R.string.device_manager_other_sessions_no_inactive_sessions_found)
+ }
+ DeviceManagerFilterType.ALL_SESSIONS -> { /* NOOP. View is not visible */ }
+ }
+
+ if (devices.isNullOrEmpty()) {
+ views.deviceListOtherSessions.isVisible = false
+ views.otherSessionsNotFoundLayout.isVisible = true
+ } else {
+ views.deviceListOtherSessions.isVisible = true
+ views.otherSessionsNotFoundLayout.isVisible = false
+ views.deviceListOtherSessions.render(devices = devices, totalNumberOfDevices = devices.size, showViewAll = false)
+ }
+ }
+
+ override fun onOtherSessionClicked(deviceId: String) {
+ viewNavigator.navigateToSessionOverview(
+ context = requireActivity(),
+ deviceId = deviceId
+ )
+ }
+
+ override fun onViewAllOtherSessionsClicked() {
+ // NOOP. We don't have this button in this screen
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationView.kt
new file mode 100644
index 0000000000..5a7d1fa910
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationView.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.res.use
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.extensions.setTextWithColoredPart
+import im.vector.app.databinding.ViewOtherSessionSecurityRecommendationBinding
+
+@AndroidEntryPoint
+class OtherSessionsSecurityRecommendationView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+ private val views: ViewOtherSessionSecurityRecommendationBinding
+ var onLearnMoreClickListener: (() -> Unit)? = null
+
+ init {
+ inflate(context, R.layout.view_other_session_security_recommendation, this)
+ views = ViewOtherSessionSecurityRecommendationBinding.bind(this)
+
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.OtherSessionsSecurityRecommendationView,
+ 0,
+ 0
+ ).use {
+ setTitle(it)
+ setDescription(it)
+ setImage(it)
+ }
+ }
+
+ private fun setTitle(typedArray: TypedArray) {
+ val title = typedArray.getString(R.styleable.OtherSessionsSecurityRecommendationView_otherSessionsRecommendationTitle)
+ setTitle(title)
+ }
+
+ private fun setTitle(title: String?) {
+ views.recommendationTitleTextView.text = title
+ }
+
+ private fun setDescription(typedArray: TypedArray) {
+ val description = typedArray.getString(R.styleable.OtherSessionsSecurityRecommendationView_otherSessionsRecommendationDescription)
+ setDescription(description)
+ }
+
+ private fun setImage(typedArray: TypedArray) {
+ val imageResource = typedArray.getResourceId(R.styleable.OtherSessionsSecurityRecommendationView_otherSessionsRecommendationImageResource, 0)
+ val backgroundTint = typedArray.getColor(R.styleable.OtherSessionsSecurityRecommendationView_otherSessionsRecommendationImageBackgroundTint, 0)
+ setImageResource(imageResource)
+ setImageBackgroundTint(backgroundTint)
+ }
+
+ private fun setImageResource(resourceId: Int) {
+ views.recommendationShieldImageView.setImageResource(resourceId)
+ }
+
+ private fun setImageBackgroundTint(backgroundTintColor: Int) {
+ views.recommendationShieldImageView.backgroundTintList = ColorStateList.valueOf(backgroundTintColor)
+ }
+
+ private fun setDescription(description: String?) {
+ val learnMore = context.getString(R.string.action_learn_more)
+ val formattedDescription = buildString {
+ append(description)
+ append(" ")
+ append(learnMore)
+ }
+
+ views.recommendationDescriptionTextView.setTextWithColoredPart(
+ fullText = formattedDescription,
+ coloredPart = learnMore,
+ underline = false
+ ) {
+ onLearnMoreClickListener?.invoke()
+ }
+ }
+
+ fun render(viewState: OtherSessionsSecurityRecommendationViewState) {
+ setTitle(viewState.title)
+ setDescription(viewState.description)
+ setImageResource(viewState.imageResourceId)
+ setImageBackgroundTint(viewState.imageTintColorResourceId)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationViewState.kt
new file mode 100644
index 0000000000..2b17cb26b3
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsSecurityRecommendationViewState.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+data class OtherSessionsSecurityRecommendationViewState(
+ val title: String,
+ val description: String,
+ val imageResourceId: Int,
+ val imageTintColorResourceId: Int,
+)
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewEvents.kt
new file mode 100644
index 0000000000..95f9c72b33
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewEvents.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import im.vector.app.core.platform.VectorViewEvents
+
+sealed class OtherSessionsViewEvents : VectorViewEvents {
+ data class Loading(val message: CharSequence? = null) : OtherSessionsViewEvents()
+ data class Failure(val throwable: Throwable) : OtherSessionsViewEvents()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt
new file mode 100644
index 0000000000..a40d95c6d9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.features.settings.devices.v2.GetDeviceFullInfoListUseCase
+import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
+import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+import kotlinx.coroutines.Job
+
+class OtherSessionsViewModel @AssistedInject constructor(
+ @Assisted initialState: OtherSessionsViewState,
+ activeSessionHolder: ActiveSessionHolder,
+ private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
+ refreshDevicesUseCase: RefreshDevicesUseCase
+) : VectorSessionsListViewModel(
+ initialState, activeSessionHolder, refreshDevicesUseCase
+) {
+
+ @AssistedFactory
+ interface Factory : MavericksAssistedViewModelFactory {
+ override fun create(initialState: OtherSessionsViewState): OtherSessionsViewModel
+ }
+
+ companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
+
+ private var observeDevicesJob: Job? = null
+
+ init {
+ observeDevices(initialState.currentFilter)
+ }
+
+ private fun observeDevices(currentFilter: DeviceManagerFilterType) {
+ observeDevicesJob?.cancel()
+ observeDevicesJob = getDeviceFullInfoListUseCase.execute(
+ filterType = currentFilter,
+ excludeCurrentDevice = true
+ )
+ .execute { async ->
+ copy(
+ devices = async,
+ )
+ }
+ }
+
+ override fun handle(action: OtherSessionsAction) {
+ when (action) {
+ is OtherSessionsAction.FilterDevices -> handleFilterDevices(action)
+ }
+ }
+
+ private fun handleFilterDevices(action: OtherSessionsAction.FilterDevices) {
+ setState {
+ copy(
+ currentFilter = action.filterType
+ )
+ }
+ observeDevices(action.filterType)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigator.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigator.kt
new file mode 100644
index 0000000000..ef1895d0ae
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigator.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import android.content.Context
+import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
+import javax.inject.Inject
+
+class OtherSessionsViewNavigator @Inject constructor() {
+
+ fun navigateToSessionOverview(context: Context, deviceId: String) {
+ context.startActivity(SessionOverviewActivity.newIntent(context, deviceId))
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt
new file mode 100644
index 0000000000..d03cba03f9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MavericksState
+import com.airbnb.mvrx.Uninitialized
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+
+data class OtherSessionsViewState(
+ val devices: Async> = Uninitialized,
+ val currentFilter: DeviceManagerFilterType = DeviceManagerFilterType.ALL_SESSIONS,
+) : MavericksState
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt
index fff81b6dc5..5a8106f2fd 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt
@@ -25,8 +25,8 @@ import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveU
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
-import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.flow.unwrap
import javax.inject.Inject
class GetDeviceFullInfoUseCase @Inject constructor(
@@ -36,7 +36,7 @@ class GetDeviceFullInfoUseCase @Inject constructor(
private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase,
) {
- fun execute(deviceId: String): Flow> {
+ fun execute(deviceId: String): Flow {
return activeSessionHolder.getSafeActiveSession()?.let { session ->
combine(
getCurrentSessionCrossSigningInfoUseCase.execute(),
@@ -58,7 +58,7 @@ class GetDeviceFullInfoUseCase @Inject constructor(
null
}
fullInfo.toOptional()
- }
+ }.unwrap()
} ?: emptyFlow()
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewEntryView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewEntryView.kt
new file mode 100644
index 0000000000..5c4f0047ce
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewEntryView.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.overview
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.res.use
+import im.vector.app.R
+import im.vector.app.core.extensions.setAttributeBackground
+import im.vector.app.databinding.ViewSessionOverviewEntryBinding
+
+class SessionOverviewEntryView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+ private val binding = ViewSessionOverviewEntryBinding.inflate(
+ LayoutInflater.from(context),
+ this
+ )
+
+ init {
+ initBackground()
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.SessionOverviewEntryView,
+ 0,
+ 0
+ ).use {
+ setTitle(it)
+ setDescription(it)
+ }
+ }
+
+ private fun initBackground() {
+ binding.root.setAttributeBackground(android.R.attr.selectableItemBackground)
+ }
+
+ private fun setTitle(typedArray: TypedArray) {
+ val title = typedArray.getString(R.styleable.SessionOverviewEntryView_sessionOverviewEntryTitle)
+ binding.sessionsOverviewEntryTitle.text = title
+ }
+
+ private fun setDescription(typedArray: TypedArray) {
+ val description = typedArray.getString(R.styleable.SessionOverviewEntryView_sessionOverviewEntryDescription)
+ binding.sessionsOverviewEntryDescription.text = description
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
index c5cd80bd3c..3fea7a9316 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
@@ -45,6 +45,8 @@ import javax.inject.Inject
class SessionOverviewFragment :
VectorBaseFragment() {
+ @Inject lateinit var viewNavigator: SessionOverviewViewNavigator
+
@Inject lateinit var dateFormatter: VectorDateFormatter
@Inject lateinit var drawableProvider: DrawableProvider
@@ -79,6 +81,7 @@ class SessionOverviewFragment :
override fun invalidate() = withState(viewModel) { state ->
updateToolbar(state.isCurrentSession)
+ updateEntryDetails(state.deviceId)
if (state.deviceInfo is Success) {
renderSessionInfo(state.isCurrentSession, state.deviceInfo.invoke())
} else {
@@ -93,6 +96,12 @@ class SessionOverviewFragment :
?.setTitle(titleResId)
}
+ private fun updateEntryDetails(deviceId: String) {
+ views.sessionOverviewEntryDetails.setOnClickListener {
+ viewNavigator.navigateToSessionDetails(requireContext(), deviceId)
+ }
+ }
+
private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) {
views.sessionOverviewInfo.isVisible = true
val viewState = SessionInfoViewState(
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
index 1a1d3640a2..bdcdc40c56 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
@@ -26,7 +26,6 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.Session
@@ -54,7 +53,6 @@ class SessionOverviewViewModel @AssistedInject constructor(
private fun observeSessionInfo(deviceId: String) {
getDeviceFullInfoUseCase.execute(deviceId)
- .mapNotNull { it.getOrNull() }
.onEach { setState { copy(deviceInfo = Success(it)) } }
.launchIn(viewModelScope)
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigator.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigator.kt
new file mode 100644
index 0000000000..ef61856255
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigator.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.overview
+
+import android.content.Context
+import im.vector.app.features.settings.devices.v2.details.SessionDetailsActivity
+import javax.inject.Inject
+
+class SessionOverviewViewNavigator @Inject constructor() {
+
+ fun navigateToSessionDetails(context: Context, deviceId: String) {
+ context.startActivity(SessionDetailsActivity.newIntent(context, deviceId))
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
index 6eede93143..0c556192ac 100644
--- a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
+++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt
@@ -60,6 +60,7 @@ class IncomingShareController @Inject constructor(
roomSummary,
data.selectedRoomIds,
RoomListDisplayMode.FILTERED,
+ singleLineLastEvent = false,
callback?.let { it::onRoomClicked },
callback?.let { it::onRoomLongClicked }
)
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt
index 910f8c5379..4787aed8ae 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt
@@ -16,25 +16,32 @@
package im.vector.app.features.spaces
+import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import im.vector.app.R
import im.vector.app.core.extensions.replaceChildFragment
+import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.FragmentSpacesBottomSheetBinding
-class SpaceListBottomSheet : BottomSheetDialogFragment() {
+class SpaceListBottomSheet : VectorBaseBottomSheetDialogFragment() {
- private lateinit var binding: FragmentSpacesBottomSheetBinding
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSpacesBottomSheetBinding {
+ return FragmentSpacesBottomSheetBinding.inflate(inflater, container, false)
+ }
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- binding = FragmentSpacesBottomSheetBinding.inflate(inflater, container, false)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (savedInstanceState == null) {
replaceChildFragment(R.id.space_list, SpaceListFragment::class.java)
}
- return binding.root
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return super.onCreateDialog(savedInstanceState).apply {
+ setPeekHeightAsScreenPercentage(0.75f)
+ }
}
companion object {
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt
index 9cd456b5d7..9786d80ae7 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt
@@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
import org.matrix.android.sdk.api.session.room.model.Membership
@@ -272,7 +273,7 @@ class SpaceListViewModel @AssistedInject constructor(
?.safeOrder()
}
val inviterIds = spaces.mapNotNull { it.inviterId }
- val inviters = inviterIds.mapNotNull { session.userService().getUser(it) }
+ val inviters = inviterIds.map { session.getUserOrDefault(it) }
copy(
asyncSpaces = asyncSpaces,
spaces = spaces,
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt
index d7db9b123b..63d63329d7 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt
@@ -189,7 +189,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
val apiResult = session.spaceService().querySpaceChildren(
spaceId = initialState.spaceId,
from = nextToken,
- knownStateList = knownResults.childrenState.orEmpty(),
+ knownStateList = knownResults.childrenState,
limit = paginationLimit
)
val newKnown = apiResult.children.mapNotNull { session.getRoomSummary(it.childRoomId) }
diff --git a/vector/src/main/res/drawable/circle_with_transparent_border.xml b/vector/src/main/res/drawable/circle_with_transparent_border.xml
new file mode 100644
index 0000000000..22b092a71e
--- /dev/null
+++ b/vector/src/main/res/drawable/circle_with_transparent_border.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/drawable/placeholder_shape_8.xml b/vector/src/main/res/drawable/placeholder_shape_8.xml
index 503389788d..4e015d4a56 100644
--- a/vector/src/main/res/drawable/placeholder_shape_8.xml
+++ b/vector/src/main/res/drawable/placeholder_shape_8.xml
@@ -2,10 +2,9 @@
-
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml b/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml
new file mode 100644
index 0000000000..a7987e70b5
--- /dev/null
+++ b/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/dialog_device_verify.xml b/vector/src/main/res/layout/dialog_device_verify.xml
index bbf346c8dc..475ffc69af 100644
--- a/vector/src/main/res/layout/dialog_device_verify.xml
+++ b/vector/src/main/res/layout/dialog_device_verify.xml
@@ -39,7 +39,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
- android:text="@string/encryption_information_device_id"
+ android:text="@string/device_manager_session_details_session_id"
android:textStyle="bold" />
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/fragment_other_sessions.xml b/vector/src/main/res/layout/fragment_other_sessions.xml
new file mode 100644
index 0000000000..037f85ad28
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_other_sessions.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_session_details.xml b/vector/src/main/res/layout/fragment_session_details.xml
new file mode 100644
index 0000000000..de0ce27798
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_session_details.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_session_overview.xml b/vector/src/main/res/layout/fragment_session_overview.xml
index 156e61673b..f4573ef85e 100644
--- a/vector/src/main/res/layout/fragment_session_overview.xml
+++ b/vector/src/main/res/layout/fragment_session_overview.xml
@@ -1,20 +1,36 @@
-
-
+
-
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml
index 9cefd6aa24..8e2daa2239 100644
--- a/vector/src/main/res/layout/fragment_settings_devices.xml
+++ b/vector/src/main/res/layout/fragment_settings_devices.xml
@@ -12,8 +12,8 @@
android:id="@+id/deviceListHeaderSectionSecurityRecommendations"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:devicesListHeaderDescription="@string/device_manager_header_section_security_recommendations_description"
- app:devicesListHeaderTitle="@string/device_manager_header_section_security_recommendations_title"
+ app:sessionsListHeaderDescription="@string/device_manager_header_section_security_recommendations_description"
+ app:sessionsListHeaderTitle="@string/device_manager_header_section_security_recommendations_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -60,8 +60,8 @@
android:id="@+id/deviceListHeaderCurrentSession"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:devicesListHeaderDescription=""
- app:devicesListHeaderTitle="@string/device_manager_current_session_title"
+ app:sessionsListHeaderDescription=""
+ app:sessionsListHeaderTitle="@string/device_manager_current_session_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListSecurityRecommendationsDivider" />
@@ -90,8 +90,8 @@
android:id="@+id/deviceListHeaderOtherSessions"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:devicesListHeaderDescription="@string/settings_sessions_other_description"
- app:devicesListHeaderTitle="@string/settings_sessions_other_title"
+ app:sessionsListHeaderDescription="@string/device_manager_sessions_other_description"
+ app:sessionsListHeaderTitle="@string/device_manager_sessions_other_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListDividerCurrentSession" />
diff --git a/vector/src/main/res/layout/fragment_user_code_show.xml b/vector/src/main/res/layout/fragment_user_code_show.xml
index 59b60d1c3b..673dcbf886 100644
--- a/vector/src/main/res/layout/fragment_user_code_show.xml
+++ b/vector/src/main/res/layout/fragment_user_code_show.xml
@@ -69,6 +69,7 @@
android:textAlignment="center"
android:textColor="?vctr_content_tertiary"
app:layout_constraintTop_toBottomOf="@id/showUserCodeCardNameText"
+ app:layout_goneMarginTop="54dp"
tools:text="@sample/users.json/data/id" />
diff --git a/vector/src/main/res/layout/item_recent_room.xml b/vector/src/main/res/layout/item_recent_room.xml
index b2d311d328..7feb8f0d16 100644
--- a/vector/src/main/res/layout/item_recent_room.xml
+++ b/vector/src/main/res/layout/item_recent_room.xml
@@ -5,7 +5,6 @@
android:id="@+id/recentRoot"
android:layout_width="84dp"
android:layout_height="wrap_content"
- android:background="?vctr_toolbar_background"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
diff --git a/vector/src/main/res/layout/item_room.xml b/vector/src/main/res/layout/item_room.xml
index ab0af18acb..a94cc0738b 100644
--- a/vector/src/main/res/layout/item_room.xml
+++ b/vector/src/main/res/layout/item_room.xml
@@ -190,7 +190,7 @@
android:layout_marginTop="3dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
- android:maxLines="2"
+ android:lines="2"
android:textAlignment="viewStart"
android:textColor="?vctr_content_secondary"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/vector/src/main/res/layout/item_room_placeholder.xml b/vector/src/main/res/layout/item_room_placeholder.xml
index ea264f2668..fa1a83c2a1 100644
--- a/vector/src/main/res/layout/item_room_placeholder.xml
+++ b/vector/src/main/res/layout/item_room_placeholder.xml
@@ -16,7 +16,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@@ -29,23 +29,20 @@
-
-
-
-
-
+
+
+
diff --git a/vector/src/main/res/layout/item_session_details_content.xml b/vector/src/main/res/layout/item_session_details_content.xml
new file mode 100644
index 0000000000..fefae65b3d
--- /dev/null
+++ b/vector/src/main/res/layout/item_session_details_content.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/item_session_details_header.xml b/vector/src/main/res/layout/item_session_details_header.xml
new file mode 100644
index 0000000000..571a541b2b
--- /dev/null
+++ b/vector/src/main/res/layout/item_session_details_header.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_other_session_security_recommendation.xml b/vector/src/main/res/layout/view_other_session_security_recommendation.xml
new file mode 100644
index 0000000000..d7597aea35
--- /dev/null
+++ b/vector/src/main/res/layout/view_other_session_security_recommendation.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_session_overview_entry.xml b/vector/src/main/res/layout/view_session_overview_entry.xml
new file mode 100644
index 0000000000..464f775192
--- /dev/null
+++ b/vector/src/main/res/layout/view_session_overview_entry.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_sessions_list_header.xml b/vector/src/main/res/layout/view_sessions_list_header.xml
index d690ee4c87..6139ff4815 100644
--- a/vector/src/main/res/layout/view_sessions_list_header.xml
+++ b/vector/src/main/res/layout/view_sessions_list_header.xml
@@ -23,10 +23,10 @@
style="@style/TextAppearance.Vector.Body.DevicesManagement"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/layout_horizontal_margin"
android:layout_marginTop="18.5dp"
- android:layout_marginEnd="40dp"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@id/sessions_list_header_title"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sessions_list_header_title"
tools:text="For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore. Learn More." />
diff --git a/vector/src/main/res/menu/menu_new_home.xml b/vector/src/main/res/menu/menu_new_home.xml
index 6cd52e5608..2292480bac 100644
--- a/vector/src/main/res/menu/menu_new_home.xml
+++ b/vector/src/main/res/menu/menu_new_home.xml
@@ -12,6 +12,11 @@
android:title="@string/invite_friends"
app:showAsAction="never" />
+
+
-
diff --git a/vector/src/main/res/raw/emoji_picker_datasource.json b/vector/src/main/res/raw/emoji_picker_datasource.json
index b233a455d2..66d3691ed3 100644
--- a/vector/src/main/res/raw/emoji_picker_datasource.json
+++ b/vector/src/main/res/raw/emoji_picker_datasource.json
@@ -1 +1 @@
-{"compressed":true,"categories":[{"id":"smileys_&_emotion","name":"Smileys & Emotion","emojis":["grinning-face","grinning-face-with-big-eyes","grinning-face-with-smiling-eyes","beaming-face-with-smiling-eyes","grinning-squinting-face","grinning-face-with-sweat","rolling-on-the-floor-laughing","face-with-tears-of-joy","slightly-smiling-face","upsidedown-face","melting-face","winking-face","smiling-face-with-smiling-eyes","smiling-face-with-halo","smiling-face-with-hearts","smiling-face-with-hearteyes","starstruck","face-blowing-a-kiss","kissing-face","smiling-face","kissing-face-with-closed-eyes","kissing-face-with-smiling-eyes","smiling-face-with-tear","face-savoring-food","face-with-tongue","winking-face-with-tongue","zany-face","squinting-face-with-tongue","moneymouth-face","smiling-face-with-open-hands","face-with-hand-over-mouth","face-with-open-eyes-and-hand-over-mouth","face-with-peeking-eye","shushing-face","thinking-face","saluting-face","zippermouth-face","face-with-raised-eyebrow","neutral-face","expressionless-face","face-without-mouth","dotted-line-face","face-in-clouds","smirking-face","unamused-face","face-with-rolling-eyes","grimacing-face","face-exhaling","lying-face","relieved-face","pensive-face","sleepy-face","drooling-face","sleeping-face","face-with-medical-mask","face-with-thermometer","face-with-headbandage","nauseated-face","face-vomiting","sneezing-face","hot-face","cold-face","woozy-face","face-with-crossedout-eyes","face-with-spiral-eyes","exploding-head","cowboy-hat-face","partying-face","disguised-face","smiling-face-with-sunglasses","nerd-face","face-with-monocle","confused-face","face-with-diagonal-mouth","worried-face","slightly-frowning-face","frowning-face","face-with-open-mouth","hushed-face","astonished-face","flushed-face","pleading-face","face-holding-back-tears","frowning-face-with-open-mouth","anguished-face","fearful-face","anxious-face-with-sweat","sad-but-relieved-face","crying-face","loudly-crying-face","face-screaming-in-fear","confounded-face","persevering-face","disappointed-face","downcast-face-with-sweat","weary-face","tired-face","yawning-face","face-with-steam-from-nose","pouting-face","angry-face","face-with-symbols-on-mouth","smiling-face-with-horns","angry-face-with-horns","skull","skull-and-crossbones","pile-of-poo","clown-face","ogre","goblin","ghost","alien","alien-monster","robot","grinning-cat","grinning-cat-with-smiling-eyes","cat-with-tears-of-joy","smiling-cat-with-hearteyes","cat-with-wry-smile","kissing-cat","weary-cat","crying-cat","pouting-cat","seenoevil-monkey","hearnoevil-monkey","speaknoevil-monkey","kiss-mark","love-letter","heart-with-arrow","heart-with-ribbon","sparkling-heart","growing-heart","beating-heart","revolving-hearts","two-hearts","heart-decoration","heart-exclamation","broken-heart","heart-on-fire","mending-heart","red-heart","orange-heart","yellow-heart","green-heart","blue-heart","purple-heart","brown-heart","black-heart","white-heart","hundred-points","anger-symbol","collision","dizzy","sweat-droplets","dashing-away","hole","bomb","speech-balloon","eye-in-speech-bubble","left-speech-bubble","right-anger-bubble","thought-balloon","zzz"]},{"id":"people_&_body","name":"People & Body","emojis":["waving-hand","raised-back-of-hand","hand-with-fingers-splayed","raised-hand","vulcan-salute","rightwards-hand","leftwards-hand","palm-down-hand","palm-up-hand","ok-hand","pinched-fingers","pinching-hand","victory-hand","crossed-fingers","hand-with-index-finger-and-thumb-crossed","loveyou-gesture","sign-of-the-horns","call-me-hand","backhand-index-pointing-left","backhand-index-pointing-right","backhand-index-pointing-up","middle-finger","backhand-index-pointing-down","index-pointing-up","index-pointing-at-the-viewer","thumbs-up","thumbs-down","raised-fist","oncoming-fist","leftfacing-fist","rightfacing-fist","clapping-hands","raising-hands","heart-hands","open-hands","palms-up-together","handshake","folded-hands","writing-hand","nail-polish","selfie","flexed-biceps","mechanical-arm","mechanical-leg","leg","foot","ear","ear-with-hearing-aid","nose","brain","anatomical-heart","lungs","tooth","bone","eyes","eye","tongue","mouth","biting-lip","baby","child","boy","girl","person","person-blond-hair","man","person-beard","man-beard","woman-beard","man-red-hair","man-curly-hair","man-white-hair","man-bald","woman","woman-red-hair","person-red-hair","woman-curly-hair","person-curly-hair","woman-white-hair","person-white-hair","woman-bald","person-bald","woman-blond-hair","man-blond-hair","older-person","old-man","old-woman","person-frowning","man-frowning","woman-frowning","person-pouting","man-pouting","woman-pouting","person-gesturing-no","man-gesturing-no","woman-gesturing-no","person-gesturing-ok","man-gesturing-ok","woman-gesturing-ok","person-tipping-hand","man-tipping-hand","woman-tipping-hand","person-raising-hand","man-raising-hand","woman-raising-hand","deaf-person","deaf-man","deaf-woman","person-bowing","man-bowing","woman-bowing","person-facepalming","man-facepalming","woman-facepalming","person-shrugging","man-shrugging","woman-shrugging","health-worker","man-health-worker","woman-health-worker","student","man-student","woman-student","teacher","man-teacher","woman-teacher","judge","man-judge","woman-judge","farmer","man-farmer","woman-farmer","cook","man-cook","woman-cook","mechanic","man-mechanic","woman-mechanic","factory-worker","man-factory-worker","woman-factory-worker","office-worker","man-office-worker","woman-office-worker","scientist","man-scientist","woman-scientist","technologist","man-technologist","woman-technologist","singer","man-singer","woman-singer","artist","man-artist","woman-artist","pilot","man-pilot","woman-pilot","astronaut","man-astronaut","woman-astronaut","firefighter","man-firefighter","woman-firefighter","police-officer","man-police-officer","woman-police-officer","detective","man-detective","woman-detective","guard","man-guard","woman-guard","ninja","construction-worker","man-construction-worker","woman-construction-worker","person-with-crown","prince","princess","person-wearing-turban","man-wearing-turban","woman-wearing-turban","person-with-skullcap","woman-with-headscarf","person-in-tuxedo","man-in-tuxedo","woman-in-tuxedo","person-with-veil","man-with-veil","woman-with-veil","pregnant-woman","pregnant-man","pregnant-person","breastfeeding","woman-feeding-baby","man-feeding-baby","person-feeding-baby","baby-angel","santa-claus","mrs-claus","mx-claus","superhero","man-superhero","woman-superhero","supervillain","man-supervillain","woman-supervillain","mage","man-mage","woman-mage","fairy","man-fairy","woman-fairy","vampire","man-vampire","woman-vampire","merperson","merman","mermaid","elf","man-elf","woman-elf","genie","man-genie","woman-genie","zombie","man-zombie","woman-zombie","troll","person-getting-massage","man-getting-massage","woman-getting-massage","person-getting-haircut","man-getting-haircut","woman-getting-haircut","person-walking","man-walking","woman-walking","person-standing","man-standing","woman-standing","person-kneeling","man-kneeling","woman-kneeling","person-with-white-cane","man-with-white-cane","woman-with-white-cane","person-in-motorized-wheelchair","man-in-motorized-wheelchair","woman-in-motorized-wheelchair","person-in-manual-wheelchair","man-in-manual-wheelchair","woman-in-manual-wheelchair","person-running","man-running","woman-running","woman-dancing","man-dancing","person-in-suit-levitating","people-with-bunny-ears","men-with-bunny-ears","women-with-bunny-ears","person-in-steamy-room","man-in-steamy-room","woman-in-steamy-room","person-climbing","man-climbing","woman-climbing","person-fencing","horse-racing","skier","snowboarder","person-golfing","man-golfing","woman-golfing","person-surfing","man-surfing","woman-surfing","person-rowing-boat","man-rowing-boat","woman-rowing-boat","person-swimming","man-swimming","woman-swimming","person-bouncing-ball","man-bouncing-ball","woman-bouncing-ball","person-lifting-weights","man-lifting-weights","woman-lifting-weights","person-biking","man-biking","woman-biking","person-mountain-biking","man-mountain-biking","woman-mountain-biking","person-cartwheeling","man-cartwheeling","woman-cartwheeling","people-wrestling","men-wrestling","women-wrestling","person-playing-water-polo","man-playing-water-polo","woman-playing-water-polo","person-playing-handball","man-playing-handball","woman-playing-handball","person-juggling","man-juggling","woman-juggling","person-in-lotus-position","man-in-lotus-position","woman-in-lotus-position","person-taking-bath","person-in-bed","people-holding-hands","women-holding-hands","woman-and-man-holding-hands","men-holding-hands","kiss","kiss-woman-man","kiss-man-man","kiss-woman-woman","couple-with-heart","couple-with-heart-woman-man","couple-with-heart-man-man","couple-with-heart-woman-woman","family","family-man-woman-boy","family-man-woman-girl","family-man-woman-girl-boy","family-man-woman-boy-boy","family-man-woman-girl-girl","family-man-man-boy","family-man-man-girl","family-man-man-girl-boy","family-man-man-boy-boy","family-man-man-girl-girl","family-woman-woman-boy","family-woman-woman-girl","family-woman-woman-girl-boy","family-woman-woman-boy-boy","family-woman-woman-girl-girl","family-man-boy","family-man-boy-boy","family-man-girl","family-man-girl-boy","family-man-girl-girl","family-woman-boy","family-woman-boy-boy","family-woman-girl","family-woman-girl-boy","family-woman-girl-girl","speaking-head","bust-in-silhouette","busts-in-silhouette","people-hugging","footprints"]},{"id":"animals_&_nature","name":"Animals & Nature","emojis":["monkey-face","monkey","gorilla","orangutan","dog-face","dog","guide-dog","service-dog","poodle","wolf","fox","raccoon","cat-face","cat","black-cat","lion","tiger-face","tiger","leopard","horse-face","horse","unicorn","zebra","deer","bison","cow-face","ox","water-buffalo","cow","pig-face","pig","boar","pig-nose","ram","ewe","goat","camel","twohump-camel","llama","giraffe","elephant","mammoth","rhinoceros","hippopotamus","mouse-face","mouse","rat","hamster","rabbit-face","rabbit","chipmunk","beaver","hedgehog","bat","bear","polar-bear","koala","panda","sloth","otter","skunk","kangaroo","badger","paw-prints","turkey","chicken","rooster","hatching-chick","baby-chick","frontfacing-baby-chick","bird","penguin","dove","eagle","duck","swan","owl","dodo","feather","flamingo","peacock","parrot","frog","crocodile","turtle","lizard","snake","dragon-face","dragon","sauropod","trex","spouting-whale","whale","dolphin","seal","fish","tropical-fish","blowfish","shark","octopus","spiral-shell","coral","snail","butterfly","bug","ant","honeybee","beetle","lady-beetle","cricket","cockroach","spider","spider-web","scorpion","mosquito","fly","worm","microbe","bouquet","cherry-blossom","white-flower","lotus","rosette","rose","wilted-flower","hibiscus","sunflower","blossom","tulip","seedling","potted-plant","evergreen-tree","deciduous-tree","palm-tree","cactus","sheaf-of-rice","herb","shamrock","four-leaf-clover","maple-leaf","fallen-leaf","leaf-fluttering-in-wind","empty-nest","nest-with-eggs"]},{"id":"food_&_drink","name":"Food & Drink","emojis":["grapes","melon","watermelon","tangerine","lemon","banana","pineapple","mango","red-apple","green-apple","pear","peach","cherries","strawberry","blueberries","kiwi-fruit","tomato","olive","coconut","avocado","eggplant","potato","carrot","ear-of-corn","hot-pepper","bell-pepper","cucumber","leafy-green","broccoli","garlic","onion","mushroom","peanuts","beans","chestnut","bread","croissant","baguette-bread","flatbread","pretzel","bagel","pancakes","waffle","cheese-wedge","meat-on-bone","poultry-leg","cut-of-meat","bacon","hamburger","french-fries","pizza","hot-dog","sandwich","taco","burrito","tamale","stuffed-flatbread","falafel","egg","cooking","shallow-pan-of-food","pot-of-food","fondue","bowl-with-spoon","green-salad","popcorn","butter","salt","canned-food","bento-box","rice-cracker","rice-ball","cooked-rice","curry-rice","steaming-bowl","spaghetti","roasted-sweet-potato","oden","sushi","fried-shrimp","fish-cake-with-swirl","moon-cake","dango","dumpling","fortune-cookie","takeout-box","crab","lobster","shrimp","squid","oyster","soft-ice-cream","shaved-ice","ice-cream","doughnut","cookie","birthday-cake","shortcake","cupcake","pie","chocolate-bar","candy","lollipop","custard","honey-pot","baby-bottle","glass-of-milk","hot-beverage","teapot","teacup-without-handle","sake","bottle-with-popping-cork","wine-glass","cocktail-glass","tropical-drink","beer-mug","clinking-beer-mugs","clinking-glasses","tumbler-glass","pouring-liquid","cup-with-straw","bubble-tea","beverage-box","mate","ice","chopsticks","fork-and-knife-with-plate","fork-and-knife","spoon","kitchen-knife","jar","amphora"]},{"id":"travel_&_places","name":"Travel & Places","emojis":["globe-showing-europeafrica","globe-showing-americas","globe-showing-asiaaustralia","globe-with-meridians","world-map","map-of-japan","compass","snowcapped-mountain","mountain","volcano","mount-fuji","camping","beach-with-umbrella","desert","desert-island","national-park","stadium","classical-building","building-construction","brick","rock","wood","hut","houses","derelict-house","house","house-with-garden","office-building","japanese-post-office","post-office","hospital","bank","hotel","love-hotel","convenience-store","school","department-store","factory","japanese-castle","castle","wedding","tokyo-tower","statue-of-liberty","church","mosque","hindu-temple","synagogue","shinto-shrine","kaaba","fountain","tent","foggy","night-with-stars","cityscape","sunrise-over-mountains","sunrise","cityscape-at-dusk","sunset","bridge-at-night","hot-springs","carousel-horse","playground-slide","ferris-wheel","roller-coaster","barber-pole","circus-tent","locomotive","railway-car","highspeed-train","bullet-train","train","metro","light-rail","station","tram","monorail","mountain-railway","tram-car","bus","oncoming-bus","trolleybus","minibus","ambulance","fire-engine","police-car","oncoming-police-car","taxi","oncoming-taxi","automobile","oncoming-automobile","sport-utility-vehicle","pickup-truck","delivery-truck","articulated-lorry","tractor","racing-car","motorcycle","motor-scooter","manual-wheelchair","motorized-wheelchair","auto-rickshaw","bicycle","kick-scooter","skateboard","roller-skate","bus-stop","motorway","railway-track","oil-drum","fuel-pump","wheel","police-car-light","horizontal-traffic-light","vertical-traffic-light","stop-sign","construction","anchor","ring-buoy","sailboat","canoe","speedboat","passenger-ship","ferry","motor-boat","ship","airplane","small-airplane","airplane-departure","airplane-arrival","parachute","seat","helicopter","suspension-railway","mountain-cableway","aerial-tramway","satellite","rocket","flying-saucer","bellhop-bell","luggage","hourglass-done","hourglass-not-done","watch","alarm-clock","stopwatch","timer-clock","mantelpiece-clock","twelve-oclock","twelvethirty","one-oclock","onethirty","two-oclock","twothirty","three-oclock","threethirty","four-oclock","fourthirty","five-oclock","fivethirty","six-oclock","sixthirty","seven-oclock","seventhirty","eight-oclock","eightthirty","nine-oclock","ninethirty","ten-oclock","tenthirty","eleven-oclock","eleventhirty","new-moon","waxing-crescent-moon","first-quarter-moon","waxing-gibbous-moon","full-moon","waning-gibbous-moon","last-quarter-moon","waning-crescent-moon","crescent-moon","new-moon-face","first-quarter-moon-face","last-quarter-moon-face","thermometer","sun","full-moon-face","sun-with-face","ringed-planet","star","glowing-star","shooting-star","milky-way","cloud","sun-behind-cloud","cloud-with-lightning-and-rain","sun-behind-small-cloud","sun-behind-large-cloud","sun-behind-rain-cloud","cloud-with-rain","cloud-with-snow","cloud-with-lightning","tornado","fog","wind-face","cyclone","rainbow","closed-umbrella","umbrella","umbrella-with-rain-drops","umbrella-on-ground","high-voltage","snowflake","snowman","snowman-without-snow","comet","fire","droplet","water-wave"]},{"id":"activities","name":"Activities","emojis":["jackolantern","christmas-tree","fireworks","sparkler","firecracker","sparkles","balloon","party-popper","confetti-ball","tanabata-tree","pine-decoration","japanese-dolls","carp-streamer","wind-chime","moon-viewing-ceremony","red-envelope","ribbon","wrapped-gift","reminder-ribbon","admission-tickets","ticket","military-medal","trophy","sports-medal","1st-place-medal","2nd-place-medal","3rd-place-medal","soccer-ball","baseball","softball","basketball","volleyball","american-football","rugby-football","tennis","flying-disc","bowling","cricket-game","field-hockey","ice-hockey","lacrosse","ping-pong","badminton","boxing-glove","martial-arts-uniform","goal-net","flag-in-hole","ice-skate","fishing-pole","diving-mask","running-shirt","skis","sled","curling-stone","bullseye","yoyo","kite","pool-8-ball","crystal-ball","magic-wand","nazar-amulet","hamsa","video-game","joystick","slot-machine","game-die","puzzle-piece","teddy-bear","piata","mirror-ball","nesting-dolls","spade-suit","heart-suit","diamond-suit","club-suit","chess-pawn","joker","mahjong-red-dragon","flower-playing-cards","performing-arts","framed-picture","artist-palette","thread","sewing-needle","yarn","knot"]},{"id":"objects","name":"Objects","emojis":["glasses","sunglasses","goggles","lab-coat","safety-vest","necktie","tshirt","jeans","scarf","gloves","coat","socks","dress","kimono","sari","onepiece-swimsuit","briefs","shorts","bikini","womans-clothes","purse","handbag","clutch-bag","shopping-bags","backpack","thong-sandal","mans-shoe","running-shoe","hiking-boot","flat-shoe","highheeled-shoe","womans-sandal","ballet-shoes","womans-boot","crown","womans-hat","top-hat","graduation-cap","billed-cap","military-helmet","rescue-workers-helmet","prayer-beads","lipstick","ring","gem-stone","muted-speaker","speaker-low-volume","speaker-medium-volume","speaker-high-volume","loudspeaker","megaphone","postal-horn","bell","bell-with-slash","musical-score","musical-note","musical-notes","studio-microphone","level-slider","control-knobs","microphone","headphone","radio","saxophone","accordion","guitar","musical-keyboard","trumpet","violin","banjo","drum","long-drum","mobile-phone","mobile-phone-with-arrow","telephone","telephone-receiver","pager","fax-machine","battery","low-battery","electric-plug","laptop","desktop-computer","printer","keyboard","computer-mouse","trackball","computer-disk","floppy-disk","optical-disk","dvd","abacus","movie-camera","film-frames","film-projector","clapper-board","television","camera","camera-with-flash","video-camera","videocassette","magnifying-glass-tilted-left","magnifying-glass-tilted-right","candle","light-bulb","flashlight","red-paper-lantern","diya-lamp","notebook-with-decorative-cover","closed-book","open-book","green-book","blue-book","orange-book","books","notebook","ledger","page-with-curl","scroll","page-facing-up","newspaper","rolledup-newspaper","bookmark-tabs","bookmark","label","money-bag","coin","yen-banknote","dollar-banknote","euro-banknote","pound-banknote","money-with-wings","credit-card","receipt","chart-increasing-with-yen","envelope","email","incoming-envelope","envelope-with-arrow","outbox-tray","inbox-tray","package","closed-mailbox-with-raised-flag","closed-mailbox-with-lowered-flag","open-mailbox-with-raised-flag","open-mailbox-with-lowered-flag","postbox","ballot-box-with-ballot","pencil","black-nib","fountain-pen","pen","paintbrush","crayon","memo","briefcase","file-folder","open-file-folder","card-index-dividers","calendar","tearoff-calendar","spiral-notepad","spiral-calendar","card-index","chart-increasing","chart-decreasing","bar-chart","clipboard","pushpin","round-pushpin","paperclip","linked-paperclips","straight-ruler","triangular-ruler","scissors","card-file-box","file-cabinet","wastebasket","locked","unlocked","locked-with-pen","locked-with-key","key","old-key","hammer","axe","pick","hammer-and-pick","hammer-and-wrench","dagger","crossed-swords","water-pistol","boomerang","bow-and-arrow","shield","carpentry-saw","wrench","screwdriver","nut-and-bolt","gear","clamp","balance-scale","white-cane","link","chains","hook","toolbox","magnet","ladder","alembic","test-tube","petri-dish","dna","microscope","telescope","satellite-antenna","syringe","drop-of-blood","pill","adhesive-bandage","crutch","stethoscope","xray","door","elevator","mirror","window","bed","couch-and-lamp","chair","toilet","plunger","shower","bathtub","mouse-trap","razor","lotion-bottle","safety-pin","broom","basket","roll-of-paper","bucket","soap","bubbles","toothbrush","sponge","fire-extinguisher","shopping-cart","cigarette","coffin","headstone","funeral-urn","moai","placard","identification-card"]},{"id":"symbols","name":"Symbols","emojis":["atm-sign","litter-in-bin-sign","potable-water","wheelchair-symbol","mens-room","womens-room","restroom","baby-symbol","water-closet","passport-control","customs","baggage-claim","left-luggage","warning","children-crossing","no-entry","prohibited","no-bicycles","no-smoking","no-littering","nonpotable-water","no-pedestrians","no-mobile-phones","no-one-under-eighteen","radioactive","biohazard","up-arrow","upright-arrow","right-arrow","downright-arrow","down-arrow","downleft-arrow","left-arrow","upleft-arrow","updown-arrow","leftright-arrow","right-arrow-curving-left","left-arrow-curving-right","right-arrow-curving-up","right-arrow-curving-down","clockwise-vertical-arrows","counterclockwise-arrows-button","back-arrow","end-arrow","on-arrow","soon-arrow","top-arrow","place-of-worship","atom-symbol","om","star-of-david","wheel-of-dharma","yin-yang","latin-cross","orthodox-cross","star-and-crescent","peace-symbol","menorah","dotted-sixpointed-star","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn","aquarius","pisces","ophiuchus","shuffle-tracks-button","repeat-button","repeat-single-button","play-button","fastforward-button","next-track-button","play-or-pause-button","reverse-button","fast-reverse-button","last-track-button","upwards-button","fast-up-button","downwards-button","fast-down-button","pause-button","stop-button","record-button","eject-button","cinema","dim-button","bright-button","antenna-bars","vibration-mode","mobile-phone-off","female-sign","male-sign","transgender-symbol","multiply","plus","minus","divide","heavy-equals-sign","infinity","double-exclamation-mark","exclamation-question-mark","red-question-mark","white-question-mark","white-exclamation-mark","red-exclamation-mark","wavy-dash","currency-exchange","heavy-dollar-sign","medical-symbol","recycling-symbol","fleurdelis","trident-emblem","name-badge","japanese-symbol-for-beginner","hollow-red-circle","check-mark-button","check-box-with-check","check-mark","cross-mark","cross-mark-button","curly-loop","double-curly-loop","part-alternation-mark","eightspoked-asterisk","eightpointed-star","sparkle","copyright","registered","trade-mark","keycap","keycap","keycap-0","keycap-1","keycap-2","keycap-3","keycap-4","keycap-5","keycap-6","keycap-7","keycap-8","keycap-9","keycap-10","input-latin-uppercase","input-latin-lowercase","input-numbers","input-symbols","input-latin-letters","a-button-blood-type","ab-button-blood-type","b-button-blood-type","cl-button","cool-button","free-button","information","id-button","circled-m","new-button","ng-button","o-button-blood-type","ok-button","p-button","sos-button","up-button","vs-button","japanese-here-button","japanese-service-charge-button","japanese-monthly-amount-button","japanese-not-free-of-charge-button","japanese-reserved-button","japanese-bargain-button","japanese-discount-button","japanese-free-of-charge-button","japanese-prohibited-button","japanese-acceptable-button","japanese-application-button","japanese-passing-grade-button","japanese-vacancy-button","japanese-congratulations-button","japanese-secret-button","japanese-open-for-business-button","japanese-no-vacancy-button","red-circle","orange-circle","yellow-circle","green-circle","blue-circle","purple-circle","brown-circle","black-circle","white-circle","red-square","orange-square","yellow-square","green-square","blue-square","purple-square","brown-square","black-large-square","white-large-square","black-medium-square","white-medium-square","black-mediumsmall-square","white-mediumsmall-square","black-small-square","white-small-square","large-orange-diamond","large-blue-diamond","small-orange-diamond","small-blue-diamond","red-triangle-pointed-up","red-triangle-pointed-down","diamond-with-a-dot","radio-button","white-square-button","black-square-button"]},{"id":"flags","name":"Flags","emojis":["chequered-flag","triangular-flag","crossed-flags","black-flag","white-flag","rainbow-flag","transgender-flag","pirate-flag","flag-ascension-island","flag-andorra","flag-united-arab-emirates","flag-afghanistan","flag-antigua--barbuda","flag-anguilla","flag-albania","flag-armenia","flag-angola","flag-antarctica","flag-argentina","flag-american-samoa","flag-austria","flag-australia","flag-aruba","flag-land-islands","flag-azerbaijan","flag-bosnia--herzegovina","flag-barbados","flag-bangladesh","flag-belgium","flag-burkina-faso","flag-bulgaria","flag-bahrain","flag-burundi","flag-benin","flag-st-barthlemy","flag-bermuda","flag-brunei","flag-bolivia","flag-caribbean-netherlands","flag-brazil","flag-bahamas","flag-bhutan","flag-bouvet-island","flag-botswana","flag-belarus","flag-belize","flag-canada","flag-cocos-keeling-islands","flag-congo--kinshasa","flag-central-african-republic","flag-congo--brazzaville","flag-switzerland","flag-cte-divoire","flag-cook-islands","flag-chile","flag-cameroon","flag-china","flag-colombia","flag-clipperton-island","flag-costa-rica","flag-cuba","flag-cape-verde","flag-curaao","flag-christmas-island","flag-cyprus","flag-czechia","flag-germany","flag-diego-garcia","flag-djibouti","flag-denmark","flag-dominica","flag-dominican-republic","flag-algeria","flag-ceuta--melilla","flag-ecuador","flag-estonia","flag-egypt","flag-western-sahara","flag-eritrea","flag-spain","flag-ethiopia","flag-european-union","flag-finland","flag-fiji","flag-falkland-islands","flag-micronesia","flag-faroe-islands","flag-france","flag-gabon","flag-united-kingdom","flag-grenada","flag-georgia","flag-french-guiana","flag-guernsey","flag-ghana","flag-gibraltar","flag-greenland","flag-gambia","flag-guinea","flag-guadeloupe","flag-equatorial-guinea","flag-greece","flag-south-georgia--south-sandwich-islands","flag-guatemala","flag-guam","flag-guineabissau","flag-guyana","flag-hong-kong-sar-china","flag-heard--mcdonald-islands","flag-honduras","flag-croatia","flag-haiti","flag-hungary","flag-canary-islands","flag-indonesia","flag-ireland","flag-israel","flag-isle-of-man","flag-india","flag-british-indian-ocean-territory","flag-iraq","flag-iran","flag-iceland","flag-italy","flag-jersey","flag-jamaica","flag-jordan","flag-japan","flag-kenya","flag-kyrgyzstan","flag-cambodia","flag-kiribati","flag-comoros","flag-st-kitts--nevis","flag-north-korea","flag-south-korea","flag-kuwait","flag-cayman-islands","flag-kazakhstan","flag-laos","flag-lebanon","flag-st-lucia","flag-liechtenstein","flag-sri-lanka","flag-liberia","flag-lesotho","flag-lithuania","flag-luxembourg","flag-latvia","flag-libya","flag-morocco","flag-monaco","flag-moldova","flag-montenegro","flag-st-martin","flag-madagascar","flag-marshall-islands","flag-north-macedonia","flag-mali","flag-myanmar-burma","flag-mongolia","flag-macao-sar-china","flag-northern-mariana-islands","flag-martinique","flag-mauritania","flag-montserrat","flag-malta","flag-mauritius","flag-maldives","flag-malawi","flag-mexico","flag-malaysia","flag-mozambique","flag-namibia","flag-new-caledonia","flag-niger","flag-norfolk-island","flag-nigeria","flag-nicaragua","flag-netherlands","flag-norway","flag-nepal","flag-nauru","flag-niue","flag-new-zealand","flag-oman","flag-panama","flag-peru","flag-french-polynesia","flag-papua-new-guinea","flag-philippines","flag-pakistan","flag-poland","flag-st-pierre--miquelon","flag-pitcairn-islands","flag-puerto-rico","flag-palestinian-territories","flag-portugal","flag-palau","flag-paraguay","flag-qatar","flag-runion","flag-romania","flag-serbia","flag-russia","flag-rwanda","flag-saudi-arabia","flag-solomon-islands","flag-seychelles","flag-sudan","flag-sweden","flag-singapore","flag-st-helena","flag-slovenia","flag-svalbard--jan-mayen","flag-slovakia","flag-sierra-leone","flag-san-marino","flag-senegal","flag-somalia","flag-suriname","flag-south-sudan","flag-so-tom--prncipe","flag-el-salvador","flag-sint-maarten","flag-syria","flag-eswatini","flag-tristan-da-cunha","flag-turks--caicos-islands","flag-chad","flag-french-southern-territories","flag-togo","flag-thailand","flag-tajikistan","flag-tokelau","flag-timorleste","flag-turkmenistan","flag-tunisia","flag-tonga","flag-turkey","flag-trinidad--tobago","flag-tuvalu","flag-taiwan","flag-tanzania","flag-ukraine","flag-uganda","flag-us-outlying-islands","flag-united-nations","flag-united-states","flag-uruguay","flag-uzbekistan","flag-vatican-city","flag-st-vincent--grenadines","flag-venezuela","flag-british-virgin-islands","flag-us-virgin-islands","flag-vietnam","flag-vanuatu","flag-wallis--futuna","flag-samoa","flag-kosovo","flag-yemen","flag-mayotte","flag-south-africa","flag-zambia","flag-zimbabwe","flag-england","flag-scotland","flag-wales"]}],"emojis":{"grinning-face":{"a":"Grinning Face","b":"1F600","j":["face","grin","smile","happy","joy",":D"]},"grinning-face-with-big-eyes":{"a":"Grinning Face with Big Eyes","b":"1F603","j":["face","mouth","open","smile","happy","joy","haha",":D",":)","funny"]},"grinning-face-with-smiling-eyes":{"a":"Grinning Face with Smiling Eyes","b":"1F604","j":["eye","face","mouth","open","smile","happy","joy","funny","haha","laugh","like",":D",":)"]},"beaming-face-with-smiling-eyes":{"a":"Beaming Face with Smiling Eyes","b":"1F601","j":["eye","face","grin","smile","happy","joy","kawaii"]},"grinning-squinting-face":{"a":"Grinning Squinting Face","b":"1F606","j":["face","laugh","mouth","satisfied","smile","happy","joy","lol","haha","glad","XD"]},"grinning-face-with-sweat":{"a":"Grinning Face with Sweat","b":"1F605","j":["cold","face","open","smile","sweat","hot","happy","laugh","relief"]},"rolling-on-the-floor-laughing":{"a":"Rolling on the Floor Laughing","b":"1F923","j":["face","floor","laugh","rofl","rolling","rotfl","laughing","lol","haha"]},"face-with-tears-of-joy":{"a":"Face with Tears of Joy","b":"1F602","j":["face","joy","laugh","tear","cry","tears","weep","happy","happytears","haha"]},"slightly-smiling-face":{"a":"Slightly Smiling Face","b":"1F642","j":["face","smile"]},"upsidedown-face":{"a":"Upside-Down Face","b":"1F643","j":["face","upside-down","upside_down_face","flipped","silly","smile"]},"melting-face":{"a":"⊛ Melting Face","b":"1FAE0","j":["disappear","dissolve","liquid","melt","hot","heat"]},"winking-face":{"a":"Winking Face","b":"1F609","j":["face","wink","happy","mischievous","secret",";)","smile","eye"]},"smiling-face-with-smiling-eyes":{"a":"Smiling Face with Smiling Eyes","b":"1F60A","j":["blush","eye","face","smile","happy","flushed","crush","embarrassed","shy","joy"]},"smiling-face-with-halo":{"a":"Smiling Face with Halo","b":"1F607","j":["angel","face","fantasy","halo","innocent","heaven"]},"smiling-face-with-hearts":{"a":"Smiling Face with Hearts","b":"1F970","j":["adore","crush","hearts","in love","face","love","like","affection","valentines","infatuation"]},"smiling-face-with-hearteyes":{"a":"Smiling Face with Heart-Eyes","b":"1F60D","j":["eye","face","love","smile","smiling face with heart-eyes","smiling_face_with_heart_eyes","like","affection","valentines","infatuation","crush","heart"]},"starstruck":{"a":"Star-Struck","b":"1F929","j":["eyes","face","grinning","star","star-struck","starry-eyed","star_struck","smile","starry"]},"face-blowing-a-kiss":{"a":"Face Blowing a Kiss","b":"1F618","j":["face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face":{"a":"Kissing Face","b":"1F617","j":["face","kiss","love","like","3","valentines","infatuation"]},"smiling-face":{"a":"Smiling Face","b":"263A","j":["face","outlined","relaxed","smile","blush","massage","happiness"]},"kissing-face-with-closed-eyes":{"a":"Kissing Face with Closed Eyes","b":"1F61A","j":["closed","eye","face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face-with-smiling-eyes":{"a":"Kissing Face with Smiling Eyes","b":"1F619","j":["eye","face","kiss","smile","affection","valentines","infatuation"]},"smiling-face-with-tear":{"a":"Smiling Face with Tear","b":"1F972","j":["grateful","proud","relieved","smiling","tear","touched","sad","cry","pretend"]},"face-savoring-food":{"a":"Face Savoring Food","b":"1F60B","j":["delicious","face","savouring","smile","yum","happy","joy","tongue","silly","yummy","nom"]},"face-with-tongue":{"a":"Face with Tongue","b":"1F61B","j":["face","tongue","prank","childish","playful","mischievous","smile"]},"winking-face-with-tongue":{"a":"Winking Face with Tongue","b":"1F61C","j":["eye","face","joke","tongue","wink","prank","childish","playful","mischievous","smile"]},"zany-face":{"a":"Zany Face","b":"1F92A","j":["eye","goofy","large","small","face","crazy"]},"squinting-face-with-tongue":{"a":"Squinting Face with Tongue","b":"1F61D","j":["eye","face","horrible","taste","tongue","prank","playful","mischievous","smile"]},"moneymouth-face":{"a":"Money-Mouth Face","b":"1F911","j":["face","money","money-mouth face","mouth","money_mouth_face","rich","dollar"]},"smiling-face-with-open-hands":{"a":"Smiling Face with Open Hands","b":"1F917","j":["face","hug","hugging","open hands","smiling face","hugging_face","smile"]},"face-with-hand-over-mouth":{"a":"Face with Hand over Mouth","b":"1F92D","j":["whoops","shock","sudden realization","surprise","face"]},"face-with-open-eyes-and-hand-over-mouth":{"a":"⊛ Face with Open Eyes and Hand over Mouth","b":"1FAE2","j":["amazement","awe","disbelief","embarrass","scared","surprise","silence","secret","shock"]},"face-with-peeking-eye":{"a":"⊛ Face with Peeking Eye","b":"1FAE3","j":["captivated","peep","stare","scared","frightening","embarrassing"]},"shushing-face":{"a":"Shushing Face","b":"1F92B","j":["quiet","shush","face","shhh"]},"thinking-face":{"a":"Thinking Face","b":"1F914","j":["face","thinking","hmmm","think","consider"]},"saluting-face":{"a":"⊛ Saluting Face","b":"1FAE1","j":["ok","salute","sunny","troops","yes","respect"]},"zippermouth-face":{"a":"Zipper-Mouth Face","b":"1F910","j":["face","mouth","zipper","zipper-mouth face","zipper_mouth_face","sealed","secret"]},"face-with-raised-eyebrow":{"a":"Face with Raised Eyebrow","b":"1F928","j":["distrust","skeptic","disapproval","disbelief","mild surprise","scepticism","face","surprise"]},"neutral-face":{"a":"Neutral Face","b":"1F610","j":["deadpan","face","meh","neutral","indifference",":|"]},"expressionless-face":{"a":"Expressionless Face","b":"1F611","j":["expressionless","face","inexpressive","meh","unexpressive","indifferent","-_-","deadpan"]},"face-without-mouth":{"a":"Face Without Mouth","b":"1F636","j":["face","mouth","quiet","silent","hellokitty"]},"dotted-line-face":{"a":"⊛ Dotted Line Face","b":"1FAE5","j":["depressed","disappear","hide","introvert","invisible","lonely","isolation","depression"]},"face-in-clouds":{"a":"Face in Clouds","b":"1F636-200D-1F32B-FE0F","j":["absentminded","face in the fog","head in clouds","shower","steam","dream"]},"smirking-face":{"a":"Smirking Face","b":"1F60F","j":["face","smirk","smile","mean","prank","smug","sarcasm"]},"unamused-face":{"a":"Unamused Face","b":"1F612","j":["face","unamused","unhappy","indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"]},"face-with-rolling-eyes":{"a":"Face with Rolling Eyes","b":"1F644","j":["eyeroll","eyes","face","rolling","frustrated"]},"grimacing-face":{"a":"Grimacing Face","b":"1F62C","j":["face","grimace","teeth"]},"face-exhaling":{"a":"Face Exhaling","b":"1F62E-200D-1F4A8","j":["exhale","gasp","groan","relief","whisper","whistle","relieve","tired","sigh"]},"lying-face":{"a":"Lying Face","b":"1F925","j":["face","lie","pinocchio"]},"relieved-face":{"a":"Relieved Face","b":"1F60C","j":["face","relieved","relaxed","phew","massage","happiness"]},"pensive-face":{"a":"Pensive Face","b":"1F614","j":["dejected","face","pensive","sad","depressed","upset"]},"sleepy-face":{"a":"Sleepy Face","b":"1F62A","j":["face","sleep","tired","rest","nap"]},"drooling-face":{"a":"Drooling Face","b":"1F924","j":["drooling","face"]},"sleeping-face":{"a":"Sleeping Face","b":"1F634","j":["face","sleep","zzz","tired","sleepy","night"]},"face-with-medical-mask":{"a":"Face with Medical Mask","b":"1F637","j":["cold","doctor","face","mask","sick","ill","disease","covid"]},"face-with-thermometer":{"a":"Face with Thermometer","b":"1F912","j":["face","ill","sick","thermometer","temperature","cold","fever","covid"]},"face-with-headbandage":{"a":"Face with Head-Bandage","b":"1F915","j":["bandage","face","face with head-bandage","hurt","injury","face_with_head_bandage","injured","clumsy"]},"nauseated-face":{"a":"Nauseated Face","b":"1F922","j":["face","nauseated","vomit","gross","green","sick","throw up","ill"]},"face-vomiting":{"a":"Face Vomiting","b":"1F92E","j":["puke","sick","vomit","face"]},"sneezing-face":{"a":"Sneezing Face","b":"1F927","j":["face","gesundheit","sneeze","sick","allergy"]},"hot-face":{"a":"Hot Face","b":"1F975","j":["feverish","heat stroke","hot","red-faced","sweating","face","heat","red"]},"cold-face":{"a":"Cold Face","b":"1F976","j":["blue-faced","cold","freezing","frostbite","icicles","face","blue","frozen"]},"woozy-face":{"a":"Woozy Face","b":"1F974","j":["dizzy","intoxicated","tipsy","uneven eyes","wavy mouth","face","wavy"]},"face-with-crossedout-eyes":{"a":"Face with Crossed-out Eyes","b":"1F635","j":["crossed-out eyes","dead","face","face with crossed-out eyes","knocked out","dizzy_face","spent","unconscious","xox","dizzy"]},"face-with-spiral-eyes":{"a":"Face with Spiral Eyes","b":"1F635-200D-1F4AB","j":["dizzy","hypnotized","spiral","trouble","whoa","sick","ill","confused","nauseous","nausea"]},"exploding-head":{"a":"Exploding Head","b":"1F92F","j":["mind blown","shocked","face","mind","blown"]},"cowboy-hat-face":{"a":"Cowboy Hat Face","b":"1F920","j":["cowboy","cowgirl","face","hat"]},"partying-face":{"a":"Partying Face","b":"1F973","j":["celebration","hat","horn","party","face","woohoo"]},"disguised-face":{"a":"Disguised Face","b":"1F978","j":["disguise","face","glasses","incognito","nose","pretent","brows","moustache"]},"smiling-face-with-sunglasses":{"a":"Smiling Face with Sunglasses","b":"1F60E","j":["bright","cool","face","sun","sunglasses","smile","summer","beach","sunglass"]},"nerd-face":{"a":"Nerd Face","b":"1F913","j":["face","geek","nerd","nerdy","dork"]},"face-with-monocle":{"a":"Face with Monocle","b":"1F9D0","j":["stuffy","wealthy","face"]},"confused-face":{"a":"Confused Face","b":"1F615","j":["confused","face","meh","indifference","huh","weird","hmmm",":/"]},"face-with-diagonal-mouth":{"a":"⊛ Face with Diagonal Mouth","b":"1FAE4","j":["disappointed","meh","skeptical","unsure","skeptic","confuse","frustrated","indifferent"]},"worried-face":{"a":"Worried Face","b":"1F61F","j":["face","worried","concern","nervous",":("]},"slightly-frowning-face":{"a":"Slightly Frowning Face","b":"1F641","j":["face","frown","frowning","disappointed","sad","upset"]},"frowning-face":{"a":"Frowning Face","b":"2639","j":["face","frown","sad","upset"]},"face-with-open-mouth":{"a":"Face with Open Mouth","b":"1F62E","j":["face","mouth","open","sympathy","surprise","impressed","wow","whoa",":O"]},"hushed-face":{"a":"Hushed Face","b":"1F62F","j":["face","hushed","stunned","surprised","woo","shh"]},"astonished-face":{"a":"Astonished Face","b":"1F632","j":["astonished","face","shocked","totally","xox","surprised","poisoned"]},"flushed-face":{"a":"Flushed Face","b":"1F633","j":["dazed","face","flushed","blush","shy","flattered"]},"pleading-face":{"a":"Pleading Face","b":"1F97A","j":["begging","mercy","puppy eyes","face"]},"face-holding-back-tears":{"a":"⊛ Face Holding Back Tears","b":"1F979","j":["angry","cry","proud","resist","sad","touched","gratitude"]},"frowning-face-with-open-mouth":{"a":"Frowning Face with Open Mouth","b":"1F626","j":["face","frown","mouth","open","aw","what"]},"anguished-face":{"a":"Anguished Face","b":"1F627","j":["anguished","face","stunned","nervous"]},"fearful-face":{"a":"Fearful Face","b":"1F628","j":["face","fear","fearful","scared","terrified","nervous","oops","huh"]},"anxious-face-with-sweat":{"a":"Anxious Face with Sweat","b":"1F630","j":["blue","cold","face","rushed","sweat","nervous"]},"sad-but-relieved-face":{"a":"Sad but Relieved Face","b":"1F625","j":["disappointed","face","relieved","whew","phew","sweat","nervous"]},"crying-face":{"a":"Crying Face","b":"1F622","j":["cry","face","sad","tear","tears","depressed","upset",":'("]},"loudly-crying-face":{"a":"Loudly Crying Face","b":"1F62D","j":["cry","face","sad","sob","tear","tears","upset","depressed"]},"face-screaming-in-fear":{"a":"Face Screaming in Fear","b":"1F631","j":["face","fear","munch","scared","scream","omg"]},"confounded-face":{"a":"Confounded Face","b":"1F616","j":["confounded","face","confused","sick","unwell","oops",":S"]},"persevering-face":{"a":"Persevering Face","b":"1F623","j":["face","persevere","sick","no","upset","oops"]},"disappointed-face":{"a":"Disappointed Face","b":"1F61E","j":["disappointed","face","sad","upset","depressed",":("]},"downcast-face-with-sweat":{"a":"Downcast Face with Sweat","b":"1F613","j":["cold","face","sweat","hot","sad","tired","exercise"]},"weary-face":{"a":"Weary Face","b":"1F629","j":["face","tired","weary","sleepy","sad","frustrated","upset"]},"tired-face":{"a":"Tired Face","b":"1F62B","j":["face","tired","sick","whine","upset","frustrated"]},"yawning-face":{"a":"Yawning Face","b":"1F971","j":["bored","tired","yawn","sleepy"]},"face-with-steam-from-nose":{"a":"Face with Steam From Nose","b":"1F624","j":["face","triumph","won","gas","phew","proud","pride"]},"pouting-face":{"a":"Pouting Face","b":"1F621","j":["angry","face","mad","pouting","rage","red","hate","despise"]},"angry-face":{"a":"Angry Face","b":"1F620","j":["anger","angry","face","mad","annoyed","frustrated"]},"face-with-symbols-on-mouth":{"a":"Face with Symbols on Mouth","b":"1F92C","j":["swearing","cursing","face","cussing","profanity","expletive"]},"smiling-face-with-horns":{"a":"Smiling Face with Horns","b":"1F608","j":["face","fairy tale","fantasy","horns","smile","devil"]},"angry-face-with-horns":{"a":"Angry Face with Horns","b":"1F47F","j":["demon","devil","face","fantasy","imp","angry","horns"]},"skull":{"a":"Skull","b":"1F480","j":["death","face","fairy tale","monster","dead","skeleton","creepy"]},"skull-and-crossbones":{"a":"Skull and Crossbones","b":"2620","j":["crossbones","death","face","monster","skull","poison","danger","deadly","scary","pirate","evil"]},"pile-of-poo":{"a":"Pile of Poo","b":"1F4A9","j":["dung","face","monster","poo","poop","hankey","shitface","fail","turd","shit"]},"clown-face":{"a":"Clown Face","b":"1F921","j":["clown","face"]},"ogre":{"a":"Ogre","b":"1F479","j":["creature","face","fairy tale","fantasy","monster","troll","red","mask","halloween","scary","creepy","devil","demon","japanese"]},"goblin":{"a":"Goblin","b":"1F47A","j":["creature","face","fairy tale","fantasy","monster","red","evil","mask","scary","creepy","japanese"]},"ghost":{"a":"Ghost","b":"1F47B","j":["creature","face","fairy tale","fantasy","monster","halloween","spooky","scary"]},"alien":{"a":"Alien","b":"1F47D","j":["creature","extraterrestrial","face","fantasy","ufo","UFO","paul","weird","outer_space"]},"alien-monster":{"a":"Alien Monster","b":"1F47E","j":["alien","creature","extraterrestrial","face","monster","ufo","game","arcade","play"]},"robot":{"a":"Robot","b":"1F916","j":["face","monster","computer","machine","bot"]},"grinning-cat":{"a":"Grinning Cat","b":"1F63A","j":["cat","face","grinning","mouth","open","smile","animal","cats","happy"]},"grinning-cat-with-smiling-eyes":{"a":"Grinning Cat with Smiling Eyes","b":"1F638","j":["cat","eye","face","grin","smile","animal","cats"]},"cat-with-tears-of-joy":{"a":"Cat with Tears of Joy","b":"1F639","j":["cat","face","joy","tear","animal","cats","haha","happy","tears"]},"smiling-cat-with-hearteyes":{"a":"Smiling Cat with Heart-Eyes","b":"1F63B","j":["cat","eye","face","heart","love","smile","smiling cat with heart-eyes","smiling_cat_with_heart_eyes","animal","like","affection","cats","valentines"]},"cat-with-wry-smile":{"a":"Cat with Wry Smile","b":"1F63C","j":["cat","face","ironic","smile","wry","animal","cats","smirk"]},"kissing-cat":{"a":"Kissing Cat","b":"1F63D","j":["cat","eye","face","kiss","animal","cats"]},"weary-cat":{"a":"Weary Cat","b":"1F640","j":["cat","face","oh","surprised","weary","animal","cats","munch","scared","scream"]},"crying-cat":{"a":"Crying Cat","b":"1F63F","j":["cat","cry","face","sad","tear","animal","tears","weep","cats","upset"]},"pouting-cat":{"a":"Pouting Cat","b":"1F63E","j":["cat","face","pouting","animal","cats"]},"seenoevil-monkey":{"a":"See-No-Evil Monkey","b":"1F648","j":["evil","face","forbidden","monkey","see","see-no-evil monkey","see_no_evil_monkey","animal","nature","haha"]},"hearnoevil-monkey":{"a":"Hear-No-Evil Monkey","b":"1F649","j":["evil","face","forbidden","hear","hear-no-evil monkey","monkey","hear_no_evil_monkey","animal","nature"]},"speaknoevil-monkey":{"a":"Speak-No-Evil Monkey","b":"1F64A","j":["evil","face","forbidden","monkey","speak","speak-no-evil monkey","speak_no_evil_monkey","animal","nature","omg"]},"kiss-mark":{"a":"Kiss Mark","b":"1F48B","j":["kiss","lips","face","love","like","affection","valentines"]},"love-letter":{"a":"Love Letter","b":"1F48C","j":["heart","letter","love","mail","email","like","affection","envelope","valentines"]},"heart-with-arrow":{"a":"Heart with Arrow","b":"1F498","j":["arrow","cupid","love","like","heart","affection","valentines"]},"heart-with-ribbon":{"a":"Heart with Ribbon","b":"1F49D","j":["ribbon","valentine","love","valentines"]},"sparkling-heart":{"a":"Sparkling Heart","b":"1F496","j":["excited","sparkle","love","like","affection","valentines"]},"growing-heart":{"a":"Growing Heart","b":"1F497","j":["excited","growing","nervous","pulse","like","love","affection","valentines","pink"]},"beating-heart":{"a":"Beating Heart","b":"1F493","j":["beating","heartbeat","pulsating","love","like","affection","valentines","pink","heart"]},"revolving-hearts":{"a":"Revolving Hearts","b":"1F49E","j":["revolving","love","like","affection","valentines"]},"two-hearts":{"a":"Two Hearts","b":"1F495","j":["love","like","affection","valentines","heart"]},"heart-decoration":{"a":"Heart Decoration","b":"1F49F","j":["heart","purple-square","love","like"]},"heart-exclamation":{"a":"Heart Exclamation","b":"2763","j":["exclamation","mark","punctuation","decoration","love"]},"broken-heart":{"a":"Broken Heart","b":"1F494","j":["break","broken","sad","sorry","heart","heartbreak"]},"heart-on-fire":{"a":"Heart on Fire","b":"2764-FE0F-200D-1F525","j":["burn","heart","love","lust","sacred heart","passionate","enthusiastic"]},"mending-heart":{"a":"Mending Heart","b":"2764-FE0F-200D-1FA79","j":["healthier","improving","mending","recovering","recuperating","well","broken heart","bandage","wounded"]},"red-heart":{"a":"Red Heart","b":"2764","j":["heart","love","like","valentines"]},"orange-heart":{"a":"Orange Heart","b":"1F9E1","j":["orange","love","like","affection","valentines"]},"yellow-heart":{"a":"Yellow Heart","b":"1F49B","j":["yellow","love","like","affection","valentines"]},"green-heart":{"a":"Green Heart","b":"1F49A","j":["green","love","like","affection","valentines"]},"blue-heart":{"a":"Blue Heart","b":"1F499","j":["blue","love","like","affection","valentines"]},"purple-heart":{"a":"Purple Heart","b":"1F49C","j":["purple","love","like","affection","valentines"]},"brown-heart":{"a":"Brown Heart","b":"1F90E","j":["brown","heart","coffee"]},"black-heart":{"a":"Black Heart","b":"1F5A4","j":["black","evil","wicked"]},"white-heart":{"a":"White Heart","b":"1F90D","j":["heart","white","pure"]},"hundred-points":{"a":"Hundred Points","b":"1F4AF","j":["100","full","hundred","score","perfect","numbers","century","exam","quiz","test","pass"]},"anger-symbol":{"a":"Anger Symbol","b":"1F4A2","j":["angry","comic","mad"]},"collision":{"a":"Collision","b":"1F4A5","j":["boom","comic","bomb","explode","explosion","blown"]},"dizzy":{"a":"Dizzy","b":"1F4AB","j":["comic","star","sparkle","shoot","magic"]},"sweat-droplets":{"a":"Sweat Droplets","b":"1F4A6","j":["comic","splashing","sweat","water","drip","oops"]},"dashing-away":{"a":"Dashing Away","b":"1F4A8","j":["comic","dash","running","wind","air","fast","shoo","fart","smoke","puff"]},"hole":{"a":"Hole","b":"1F573","j":["embarrassing"]},"bomb":{"a":"Bomb","b":"1F4A3","j":["comic","boom","explode","explosion","terrorism"]},"speech-balloon":{"a":"Speech Balloon","b":"1F4AC","j":["balloon","bubble","comic","dialog","speech","words","message","talk","chatting"]},"eye-in-speech-bubble":{"a":"Eye in Speech Bubble","b":"1F441-FE0F-200D-1F5E8-FE0F","j":["eye","speech bubble","witness","info"]},"left-speech-bubble":{"a":"Left Speech Bubble","b":"1F5E8","j":["dialog","speech","words","message","talk","chatting"]},"right-anger-bubble":{"a":"Right Anger Bubble","b":"1F5EF","j":["angry","balloon","bubble","mad","caption","speech","thinking"]},"thought-balloon":{"a":"Thought Balloon","b":"1F4AD","j":["balloon","bubble","comic","thought","cloud","speech","thinking","dream"]},"zzz":{"a":"Zzz","b":"1F4A4","j":["comic","sleep","sleepy","tired","dream"]},"waving-hand":{"a":"Waving Hand","b":"1F44B","j":["hand","wave","waving","hands","gesture","goodbye","solong","farewell","hello","hi","palm"]},"raised-back-of-hand":{"a":"Raised Back of Hand","b":"1F91A","j":["backhand","raised","fingers"]},"hand-with-fingers-splayed":{"a":"Hand with Fingers Splayed","b":"1F590","j":["finger","hand","splayed","fingers","palm"]},"raised-hand":{"a":"Raised Hand","b":"270B","j":["hand","high 5","high five","fingers","stop","highfive","palm","ban"]},"vulcan-salute":{"a":"Vulcan Salute","b":"1F596","j":["finger","hand","spock","vulcan","fingers","star trek"]},"rightwards-hand":{"a":"⊛ Rightwards Hand","b":"1FAF1","j":["hand","right","rightward","palm","offer"]},"leftwards-hand":{"a":"⊛ Leftwards Hand","b":"1FAF2","j":["hand","left","leftward","palm","offer"]},"palm-down-hand":{"a":"⊛ Palm Down Hand","b":"1FAF3","j":["dismiss","drop","shoo","palm"]},"palm-up-hand":{"a":"⊛ Palm Up Hand","b":"1FAF4","j":["beckon","catch","come","offer","lift","demand"]},"ok-hand":{"a":"Ok Hand","b":"1F44C","j":["hand","OK","fingers","limbs","perfect","ok","okay"]},"pinched-fingers":{"a":"Pinched Fingers","b":"1F90C","j":["fingers","hand gesture","interrogation","pinched","sarcastic","size","tiny","small"]},"pinching-hand":{"a":"Pinching Hand","b":"1F90F","j":["small amount","tiny","small","size"]},"victory-hand":{"a":"Victory Hand","b":"270C","j":["hand","v","victory","fingers","ohyeah","peace","two"]},"crossed-fingers":{"a":"Crossed Fingers","b":"1F91E","j":["cross","finger","hand","luck","good","lucky"]},"hand-with-index-finger-and-thumb-crossed":{"a":"⊛ Hand with Index Finger and Thumb Crossed","b":"1FAF0","j":["expensive","heart","love","money","snap"]},"loveyou-gesture":{"a":"Love-You Gesture","b":"1F91F","j":["hand","ILY","love-you gesture","love_you_gesture","fingers","gesture"]},"sign-of-the-horns":{"a":"Sign of the Horns","b":"1F918","j":["finger","hand","horns","rock-on","fingers","evil_eye","sign_of_horns","rock_on"]},"call-me-hand":{"a":"Call Me Hand","b":"1F919","j":["call","hand","hands","gesture","shaka"]},"backhand-index-pointing-left":{"a":"Backhand Index Pointing Left","b":"1F448","j":["backhand","finger","hand","index","point","direction","fingers","left"]},"backhand-index-pointing-right":{"a":"Backhand Index Pointing Right","b":"1F449","j":["backhand","finger","hand","index","point","fingers","direction","right"]},"backhand-index-pointing-up":{"a":"Backhand Index Pointing Up","b":"1F446","j":["backhand","finger","hand","point","up","fingers","direction"]},"middle-finger":{"a":"Middle Finger","b":"1F595","j":["finger","hand","fingers","rude","middle","flipping"]},"backhand-index-pointing-down":{"a":"Backhand Index Pointing Down","b":"1F447","j":["backhand","down","finger","hand","point","fingers","direction"]},"index-pointing-up":{"a":"Index Pointing Up","b":"261D","j":["finger","hand","index","point","up","fingers","direction"]},"index-pointing-at-the-viewer":{"a":"⊛ Index Pointing at the Viewer","b":"1FAF5","j":["point","you","recruit"]},"thumbs-up":{"a":"Thumbs Up","b":"1F44D","j":["+1","hand","thumb","up","thumbsup","yes","awesome","good","agree","accept","cool","like"]},"thumbs-down":{"a":"Thumbs Down","b":"1F44E","j":["-1","down","hand","thumb","thumbsdown","no","dislike"]},"raised-fist":{"a":"Raised Fist","b":"270A","j":["clenched","fist","hand","punch","fingers","grasp"]},"oncoming-fist":{"a":"Oncoming Fist","b":"1F44A","j":["clenched","fist","hand","punch","angry","violence","hit","attack"]},"leftfacing-fist":{"a":"Left-Facing Fist","b":"1F91B","j":["fist","left-facing fist","leftwards","left_facing_fist","hand","fistbump"]},"rightfacing-fist":{"a":"Right-Facing Fist","b":"1F91C","j":["fist","right-facing fist","rightwards","right_facing_fist","hand","fistbump"]},"clapping-hands":{"a":"Clapping Hands","b":"1F44F","j":["clap","hand","hands","praise","applause","congrats","yay"]},"raising-hands":{"a":"Raising Hands","b":"1F64C","j":["celebration","gesture","hand","hooray","raised","yea","hands"]},"heart-hands":{"a":"⊛ Heart Hands","b":"1FAF6","j":["love","appreciation","support"]},"open-hands":{"a":"Open Hands","b":"1F450","j":["hand","open","fingers","butterfly","hands"]},"palms-up-together":{"a":"Palms Up Together","b":"1F932","j":["prayer","cupped hands","hands","gesture","cupped"]},"handshake":{"a":"Handshake","b":"1F91D","j":["agreement","hand","meeting","shake"]},"folded-hands":{"a":"Folded Hands","b":"1F64F","j":["ask","hand","high 5","high five","please","pray","thanks","hope","wish","namaste","highfive","thank you","appreciate"]},"writing-hand":{"a":"Writing Hand","b":"270D","j":["hand","write","lower_left_ballpoint_pen","stationery","compose"]},"nail-polish":{"a":"Nail Polish","b":"1F485","j":["care","cosmetics","manicure","nail","polish","beauty","finger","fashion"]},"selfie":{"a":"Selfie","b":"1F933","j":["camera","phone"]},"flexed-biceps":{"a":"Flexed Biceps","b":"1F4AA","j":["biceps","comic","flex","muscle","arm","hand","summer","strong"]},"mechanical-arm":{"a":"Mechanical Arm","b":"1F9BE","j":["accessibility","prosthetic"]},"mechanical-leg":{"a":"Mechanical Leg","b":"1F9BF","j":["accessibility","prosthetic"]},"leg":{"a":"Leg","b":"1F9B5","j":["kick","limb"]},"foot":{"a":"Foot","b":"1F9B6","j":["kick","stomp"]},"ear":{"a":"Ear","b":"1F442","j":["body","face","hear","sound","listen"]},"ear-with-hearing-aid":{"a":"Ear with Hearing Aid","b":"1F9BB","j":["accessibility","hard of hearing"]},"nose":{"a":"Nose","b":"1F443","j":["body","smell","sniff"]},"brain":{"a":"Brain","b":"1F9E0","j":["intelligent","smart"]},"anatomical-heart":{"a":"Anatomical Heart","b":"1FAC0","j":["anatomical","cardiology","heart","organ","pulse","health","heartbeat"]},"lungs":{"a":"Lungs","b":"1FAC1","j":["breath","exhalation","inhalation","organ","respiration","breathe"]},"tooth":{"a":"Tooth","b":"1F9B7","j":["dentist","teeth"]},"bone":{"a":"Bone","b":"1F9B4","j":["skeleton"]},"eyes":{"a":"Eyes","b":"1F440","j":["eye","face","look","watch","stalk","peek","see"]},"eye":{"a":"Eye","b":"1F441","j":["body","face","look","see","watch","stare"]},"tongue":{"a":"Tongue","b":"1F445","j":["body","mouth","playful"]},"mouth":{"a":"Mouth","b":"1F444","j":["lips","kiss"]},"biting-lip":{"a":"⊛ Biting Lip","b":"1FAE6","j":["anxious","fear","flirting","nervous","uncomfortable","worried","flirt","sexy","pain","worry"]},"baby":{"a":"Baby","b":"1F476","j":["young","child","boy","girl","toddler"]},"child":{"a":"Child","b":"1F9D2","j":["gender-neutral","unspecified gender","young"]},"boy":{"a":"Boy","b":"1F466","j":["young","man","male","guy","teenager"]},"girl":{"a":"Girl","b":"1F467","j":["Virgo","young","zodiac","female","woman","teenager"]},"person":{"a":"Person","b":"1F9D1","j":["adult","gender-neutral","unspecified gender"]},"person-blond-hair":{"a":"Person: Blond Hair","b":"1F471","j":["blond","blond-haired person","hair","person: blond hair","hairstyle"]},"man":{"a":"Man","b":"1F468","j":["adult","mustache","father","dad","guy","classy","sir","moustache"]},"person-beard":{"a":"Person: Beard","b":"1F9D4","j":["beard","person","person: beard","bewhiskered","man_beard"]},"man-beard":{"a":"Man: Beard","b":"1F9D4-200D-2642-FE0F","j":["beard","man","man: beard","facial hair"]},"woman-beard":{"a":"Woman: Beard","b":"1F9D4-200D-2640-FE0F","j":["beard","woman","woman: beard","facial hair"]},"man-red-hair":{"a":"Man: Red Hair","b":"1F468-200D-1F9B0","j":["adult","man","red hair","hairstyle"]},"man-curly-hair":{"a":"Man: Curly Hair","b":"1F468-200D-1F9B1","j":["adult","curly hair","man","hairstyle"]},"man-white-hair":{"a":"Man: White Hair","b":"1F468-200D-1F9B3","j":["adult","man","white hair","old","elder"]},"man-bald":{"a":"Man: Bald","b":"1F468-200D-1F9B2","j":["adult","bald","man","hairless"]},"woman":{"a":"Woman","b":"1F469","j":["adult","female","girls","lady"]},"woman-red-hair":{"a":"Woman: Red Hair","b":"1F469-200D-1F9B0","j":["adult","red hair","woman","hairstyle"]},"person-red-hair":{"a":"Person: Red Hair","b":"1F9D1-200D-1F9B0","j":["adult","gender-neutral","person","red hair","unspecified gender","hairstyle"]},"woman-curly-hair":{"a":"Woman: Curly Hair","b":"1F469-200D-1F9B1","j":["adult","curly hair","woman","hairstyle"]},"person-curly-hair":{"a":"Person: Curly Hair","b":"1F9D1-200D-1F9B1","j":["adult","curly hair","gender-neutral","person","unspecified gender","hairstyle"]},"woman-white-hair":{"a":"Woman: White Hair","b":"1F469-200D-1F9B3","j":["adult","white hair","woman","old","elder"]},"person-white-hair":{"a":"Person: White Hair","b":"1F9D1-200D-1F9B3","j":["adult","gender-neutral","person","unspecified gender","white hair","elder","old"]},"woman-bald":{"a":"Woman: Bald","b":"1F469-200D-1F9B2","j":["adult","bald","woman","hairless"]},"person-bald":{"a":"Person: Bald","b":"1F9D1-200D-1F9B2","j":["adult","bald","gender-neutral","person","unspecified gender","hairless"]},"woman-blond-hair":{"a":"Woman: Blond Hair","b":"1F471-200D-2640-FE0F","j":["blond-haired woman","blonde","hair","woman","woman: blond hair","female","girl","person"]},"man-blond-hair":{"a":"Man: Blond Hair","b":"1F471-200D-2642-FE0F","j":["blond","blond-haired man","hair","man","man: blond hair","male","boy","blonde","guy","person"]},"older-person":{"a":"Older Person","b":"1F9D3","j":["adult","gender-neutral","old","unspecified gender","human","elder","senior"]},"old-man":{"a":"Old Man","b":"1F474","j":["adult","man","old","human","male","men","elder","senior"]},"old-woman":{"a":"Old Woman","b":"1F475","j":["adult","old","woman","human","female","women","lady","elder","senior"]},"person-frowning":{"a":"Person Frowning","b":"1F64D","j":["frown","gesture","worried"]},"man-frowning":{"a":"Man Frowning","b":"1F64D-200D-2642-FE0F","j":["frowning","gesture","man","male","boy","sad","depressed","discouraged","unhappy"]},"woman-frowning":{"a":"Woman Frowning","b":"1F64D-200D-2640-FE0F","j":["frowning","gesture","woman","female","girl","sad","depressed","discouraged","unhappy"]},"person-pouting":{"a":"Person Pouting","b":"1F64E","j":["gesture","pouting","upset"]},"man-pouting":{"a":"Man Pouting","b":"1F64E-200D-2642-FE0F","j":["gesture","man","pouting","male","boy"]},"woman-pouting":{"a":"Woman Pouting","b":"1F64E-200D-2640-FE0F","j":["gesture","pouting","woman","female","girl"]},"person-gesturing-no":{"a":"Person Gesturing No","b":"1F645","j":["forbidden","gesture","hand","person gesturing NO","prohibited","decline"]},"man-gesturing-no":{"a":"Man Gesturing No","b":"1F645-200D-2642-FE0F","j":["forbidden","gesture","hand","man","man gesturing NO","prohibited","male","boy","nope"]},"woman-gesturing-no":{"a":"Woman Gesturing No","b":"1F645-200D-2640-FE0F","j":["forbidden","gesture","hand","prohibited","woman","woman gesturing NO","female","girl","nope"]},"person-gesturing-ok":{"a":"Person Gesturing Ok","b":"1F646","j":["gesture","hand","OK","person gesturing OK","agree"]},"man-gesturing-ok":{"a":"Man Gesturing Ok","b":"1F646-200D-2642-FE0F","j":["gesture","hand","man","man gesturing OK","OK","men","boy","male","blue","human"]},"woman-gesturing-ok":{"a":"Woman Gesturing Ok","b":"1F646-200D-2640-FE0F","j":["gesture","hand","OK","woman","woman gesturing OK","women","girl","female","pink","human"]},"person-tipping-hand":{"a":"Person Tipping Hand","b":"1F481","j":["hand","help","information","sassy","tipping"]},"man-tipping-hand":{"a":"Man Tipping Hand","b":"1F481-200D-2642-FE0F","j":["man","sassy","tipping hand","male","boy","human","information"]},"woman-tipping-hand":{"a":"Woman Tipping Hand","b":"1F481-200D-2640-FE0F","j":["sassy","tipping hand","woman","female","girl","human","information"]},"person-raising-hand":{"a":"Person Raising Hand","b":"1F64B","j":["gesture","hand","happy","raised","question"]},"man-raising-hand":{"a":"Man Raising Hand","b":"1F64B-200D-2642-FE0F","j":["gesture","man","raising hand","male","boy"]},"woman-raising-hand":{"a":"Woman Raising Hand","b":"1F64B-200D-2640-FE0F","j":["gesture","raising hand","woman","female","girl"]},"deaf-person":{"a":"Deaf Person","b":"1F9CF","j":["accessibility","deaf","ear","hear"]},"deaf-man":{"a":"Deaf Man","b":"1F9CF-200D-2642-FE0F","j":["deaf","man","accessibility"]},"deaf-woman":{"a":"Deaf Woman","b":"1F9CF-200D-2640-FE0F","j":["deaf","woman","accessibility"]},"person-bowing":{"a":"Person Bowing","b":"1F647","j":["apology","bow","gesture","sorry","respectiful"]},"man-bowing":{"a":"Man Bowing","b":"1F647-200D-2642-FE0F","j":["apology","bowing","favor","gesture","man","sorry","male","boy"]},"woman-bowing":{"a":"Woman Bowing","b":"1F647-200D-2640-FE0F","j":["apology","bowing","favor","gesture","sorry","woman","female","girl"]},"person-facepalming":{"a":"Person Facepalming","b":"1F926","j":["disbelief","exasperation","face","palm","disappointed"]},"man-facepalming":{"a":"Man Facepalming","b":"1F926-200D-2642-FE0F","j":["disbelief","exasperation","facepalm","man","male","boy"]},"woman-facepalming":{"a":"Woman Facepalming","b":"1F926-200D-2640-FE0F","j":["disbelief","exasperation","facepalm","woman","female","girl"]},"person-shrugging":{"a":"Person Shrugging","b":"1F937","j":["doubt","ignorance","indifference","shrug","regardless"]},"man-shrugging":{"a":"Man Shrugging","b":"1F937-200D-2642-FE0F","j":["doubt","ignorance","indifference","man","shrug","male","boy","confused","indifferent"]},"woman-shrugging":{"a":"Woman Shrugging","b":"1F937-200D-2640-FE0F","j":["doubt","ignorance","indifference","shrug","woman","female","girl","confused","indifferent"]},"health-worker":{"a":"Health Worker","b":"1F9D1-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","hospital"]},"man-health-worker":{"a":"Man Health Worker","b":"1F468-200D-2695-FE0F","j":["doctor","healthcare","man","nurse","therapist","human"]},"woman-health-worker":{"a":"Woman Health Worker","b":"1F469-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","woman","human"]},"student":{"a":"Student","b":"1F9D1-200D-1F393","j":["graduate","learn"]},"man-student":{"a":"Man Student","b":"1F468-200D-1F393","j":["graduate","man","student","human"]},"woman-student":{"a":"Woman Student","b":"1F469-200D-1F393","j":["graduate","student","woman","human"]},"teacher":{"a":"Teacher","b":"1F9D1-200D-1F3EB","j":["instructor","professor"]},"man-teacher":{"a":"Man Teacher","b":"1F468-200D-1F3EB","j":["instructor","man","professor","teacher","human"]},"woman-teacher":{"a":"Woman Teacher","b":"1F469-200D-1F3EB","j":["instructor","professor","teacher","woman","human"]},"judge":{"a":"Judge","b":"1F9D1-200D-2696-FE0F","j":["justice","scales","law"]},"man-judge":{"a":"Man Judge","b":"1F468-200D-2696-FE0F","j":["judge","justice","man","scales","court","human"]},"woman-judge":{"a":"Woman Judge","b":"1F469-200D-2696-FE0F","j":["judge","justice","scales","woman","court","human"]},"farmer":{"a":"Farmer","b":"1F9D1-200D-1F33E","j":["gardener","rancher","crops"]},"man-farmer":{"a":"Man Farmer","b":"1F468-200D-1F33E","j":["farmer","gardener","man","rancher","human"]},"woman-farmer":{"a":"Woman Farmer","b":"1F469-200D-1F33E","j":["farmer","gardener","rancher","woman","human"]},"cook":{"a":"Cook","b":"1F9D1-200D-1F373","j":["chef","food","kitchen","culinary"]},"man-cook":{"a":"Man Cook","b":"1F468-200D-1F373","j":["chef","cook","man","human"]},"woman-cook":{"a":"Woman Cook","b":"1F469-200D-1F373","j":["chef","cook","woman","human"]},"mechanic":{"a":"Mechanic","b":"1F9D1-200D-1F527","j":["electrician","plumber","tradesperson","worker","technician"]},"man-mechanic":{"a":"Man Mechanic","b":"1F468-200D-1F527","j":["electrician","man","mechanic","plumber","tradesperson","human","wrench"]},"woman-mechanic":{"a":"Woman Mechanic","b":"1F469-200D-1F527","j":["electrician","mechanic","plumber","tradesperson","woman","human","wrench"]},"factory-worker":{"a":"Factory Worker","b":"1F9D1-200D-1F3ED","j":["assembly","factory","industrial","worker","labor"]},"man-factory-worker":{"a":"Man Factory Worker","b":"1F468-200D-1F3ED","j":["assembly","factory","industrial","man","worker","human"]},"woman-factory-worker":{"a":"Woman Factory Worker","b":"1F469-200D-1F3ED","j":["assembly","factory","industrial","woman","worker","human"]},"office-worker":{"a":"Office Worker","b":"1F9D1-200D-1F4BC","j":["architect","business","manager","white-collar"]},"man-office-worker":{"a":"Man Office Worker","b":"1F468-200D-1F4BC","j":["architect","business","man","manager","white-collar","human"]},"woman-office-worker":{"a":"Woman Office Worker","b":"1F469-200D-1F4BC","j":["architect","business","manager","white-collar","woman","human"]},"scientist":{"a":"Scientist","b":"1F9D1-200D-1F52C","j":["biologist","chemist","engineer","physicist","chemistry"]},"man-scientist":{"a":"Man Scientist","b":"1F468-200D-1F52C","j":["biologist","chemist","engineer","man","physicist","scientist","human"]},"woman-scientist":{"a":"Woman Scientist","b":"1F469-200D-1F52C","j":["biologist","chemist","engineer","physicist","scientist","woman","human"]},"technologist":{"a":"Technologist","b":"1F9D1-200D-1F4BB","j":["coder","developer","inventor","software","computer"]},"man-technologist":{"a":"Man Technologist","b":"1F468-200D-1F4BB","j":["coder","developer","inventor","man","software","technologist","engineer","programmer","human","laptop","computer"]},"woman-technologist":{"a":"Woman Technologist","b":"1F469-200D-1F4BB","j":["coder","developer","inventor","software","technologist","woman","engineer","programmer","human","laptop","computer"]},"singer":{"a":"Singer","b":"1F9D1-200D-1F3A4","j":["actor","entertainer","rock","star","song","artist","performer"]},"man-singer":{"a":"Man Singer","b":"1F468-200D-1F3A4","j":["actor","entertainer","man","rock","singer","star","rockstar","human"]},"woman-singer":{"a":"Woman Singer","b":"1F469-200D-1F3A4","j":["actor","entertainer","rock","singer","star","woman","rockstar","human"]},"artist":{"a":"Artist","b":"1F9D1-200D-1F3A8","j":["palette","painting","draw","creativity"]},"man-artist":{"a":"Man Artist","b":"1F468-200D-1F3A8","j":["artist","man","palette","painter","human"]},"woman-artist":{"a":"Woman Artist","b":"1F469-200D-1F3A8","j":["artist","palette","woman","painter","human"]},"pilot":{"a":"Pilot","b":"1F9D1-200D-2708-FE0F","j":["plane","fly","airplane"]},"man-pilot":{"a":"Man Pilot","b":"1F468-200D-2708-FE0F","j":["man","pilot","plane","aviator","human"]},"woman-pilot":{"a":"Woman Pilot","b":"1F469-200D-2708-FE0F","j":["pilot","plane","woman","aviator","human"]},"astronaut":{"a":"Astronaut","b":"1F9D1-200D-1F680","j":["rocket","outerspace"]},"man-astronaut":{"a":"Man Astronaut","b":"1F468-200D-1F680","j":["astronaut","man","rocket","space","human"]},"woman-astronaut":{"a":"Woman Astronaut","b":"1F469-200D-1F680","j":["astronaut","rocket","woman","space","human"]},"firefighter":{"a":"Firefighter","b":"1F9D1-200D-1F692","j":["firetruck","fire"]},"man-firefighter":{"a":"Man Firefighter","b":"1F468-200D-1F692","j":["firefighter","firetruck","man","fireman","human"]},"woman-firefighter":{"a":"Woman Firefighter","b":"1F469-200D-1F692","j":["firefighter","firetruck","woman","fireman","human"]},"police-officer":{"a":"Police Officer","b":"1F46E","j":["cop","officer","police"]},"man-police-officer":{"a":"Man Police Officer","b":"1F46E-200D-2642-FE0F","j":["cop","man","officer","police","law","legal","enforcement","arrest","911"]},"woman-police-officer":{"a":"Woman Police Officer","b":"1F46E-200D-2640-FE0F","j":["cop","officer","police","woman","law","legal","enforcement","arrest","911","female"]},"detective":{"a":"Detective","b":"1F575","j":["sleuth","spy","human"]},"man-detective":{"a":"Man Detective","b":"1F575-FE0F-200D-2642-FE0F","j":["detective","man","sleuth","spy","crime"]},"woman-detective":{"a":"Woman Detective","b":"1F575-FE0F-200D-2640-FE0F","j":["detective","sleuth","spy","woman","human","female"]},"guard":{"a":"Guard","b":"1F482","j":["protect"]},"man-guard":{"a":"Man Guard","b":"1F482-200D-2642-FE0F","j":["guard","man","uk","gb","british","male","guy","royal"]},"woman-guard":{"a":"Woman Guard","b":"1F482-200D-2640-FE0F","j":["guard","woman","uk","gb","british","female","royal"]},"ninja":{"a":"Ninja","b":"1F977","j":["fighter","hidden","stealth","ninjutsu","skills","japanese"]},"construction-worker":{"a":"Construction Worker","b":"1F477","j":["construction","hat","worker","labor","build"]},"man-construction-worker":{"a":"Man Construction Worker","b":"1F477-200D-2642-FE0F","j":["construction","man","worker","male","human","wip","guy","build","labor"]},"woman-construction-worker":{"a":"Woman Construction Worker","b":"1F477-200D-2640-FE0F","j":["construction","woman","worker","female","human","wip","build","labor"]},"person-with-crown":{"a":"⊛ Person with Crown","b":"1FAC5","j":["monarch","noble","regal","royalty","power"]},"prince":{"a":"Prince","b":"1F934","j":["boy","man","male","crown","royal","king"]},"princess":{"a":"Princess","b":"1F478","j":["fairy tale","fantasy","girl","woman","female","blond","crown","royal","queen"]},"person-wearing-turban":{"a":"Person Wearing Turban","b":"1F473","j":["turban","headdress"]},"man-wearing-turban":{"a":"Man Wearing Turban","b":"1F473-200D-2642-FE0F","j":["man","turban","male","indian","hinduism","arabs"]},"woman-wearing-turban":{"a":"Woman Wearing Turban","b":"1F473-200D-2640-FE0F","j":["turban","woman","female","indian","hinduism","arabs"]},"person-with-skullcap":{"a":"Person with Skullcap","b":"1F472","j":["cap","gua pi mao","hat","person","skullcap","man_with_skullcap","male","boy","chinese"]},"woman-with-headscarf":{"a":"Woman with Headscarf","b":"1F9D5","j":["headscarf","hijab","mantilla","tichel","bandana","head kerchief","female"]},"person-in-tuxedo":{"a":"Person in Tuxedo","b":"1F935","j":["groom","person","tuxedo","man_in_tuxedo","couple","marriage","wedding"]},"man-in-tuxedo":{"a":"Man in Tuxedo","b":"1F935-200D-2642-FE0F","j":["man","tuxedo","formal","fashion"]},"woman-in-tuxedo":{"a":"Woman in Tuxedo","b":"1F935-200D-2640-FE0F","j":["tuxedo","woman","formal","fashion"]},"person-with-veil":{"a":"Person with Veil","b":"1F470","j":["bride","person","veil","wedding","bride_with_veil","couple","marriage","woman"]},"man-with-veil":{"a":"Man with Veil","b":"1F470-200D-2642-FE0F","j":["man","veil","wedding","marriage"]},"woman-with-veil":{"a":"Woman with Veil","b":"1F470-200D-2640-FE0F","j":["veil","woman","wedding","marriage"]},"pregnant-woman":{"a":"Pregnant Woman","b":"1F930","j":["pregnant","woman","baby"]},"pregnant-man":{"a":"⊛ Pregnant Man","b":"1FAC3","j":["belly","bloated","full","pregnant","baby"]},"pregnant-person":{"a":"⊛ Pregnant Person","b":"1FAC4","j":["belly","bloated","full","pregnant","baby"]},"breastfeeding":{"a":"Breast-Feeding","b":"1F931","j":["baby","breast","breast-feeding","nursing","breast_feeding"]},"woman-feeding-baby":{"a":"Woman Feeding Baby","b":"1F469-200D-1F37C","j":["baby","feeding","nursing","woman","birth","food"]},"man-feeding-baby":{"a":"Man Feeding Baby","b":"1F468-200D-1F37C","j":["baby","feeding","man","nursing","birth","food"]},"person-feeding-baby":{"a":"Person Feeding Baby","b":"1F9D1-200D-1F37C","j":["baby","feeding","nursing","person","birth","food"]},"baby-angel":{"a":"Baby Angel","b":"1F47C","j":["angel","baby","face","fairy tale","fantasy","heaven","wings","halo"]},"santa-claus":{"a":"Santa Claus","b":"1F385","j":["celebration","Christmas","claus","father","santa","festival","man","male","xmas","father christmas"]},"mrs-claus":{"a":"Mrs. Claus","b":"1F936","j":["celebration","Christmas","claus","mother","Mrs.","woman","female","xmas","mother christmas"]},"mx-claus":{"a":"Mx Claus","b":"1F9D1-200D-1F384","j":["Claus, christmas","christmas"]},"superhero":{"a":"Superhero","b":"1F9B8","j":["good","hero","heroine","superpower","marvel"]},"man-superhero":{"a":"Man Superhero","b":"1F9B8-200D-2642-FE0F","j":["good","hero","man","superpower","male","superpowers"]},"woman-superhero":{"a":"Woman Superhero","b":"1F9B8-200D-2640-FE0F","j":["good","hero","heroine","superpower","woman","female","superpowers"]},"supervillain":{"a":"Supervillain","b":"1F9B9","j":["criminal","evil","superpower","villain","marvel"]},"man-supervillain":{"a":"Man Supervillain","b":"1F9B9-200D-2642-FE0F","j":["criminal","evil","man","superpower","villain","male","bad","hero","superpowers"]},"woman-supervillain":{"a":"Woman Supervillain","b":"1F9B9-200D-2640-FE0F","j":["criminal","evil","superpower","villain","woman","female","bad","heroine","superpowers"]},"mage":{"a":"Mage","b":"1F9D9","j":["sorcerer","sorceress","witch","wizard","magic"]},"man-mage":{"a":"Man Mage","b":"1F9D9-200D-2642-FE0F","j":["sorcerer","wizard","man","male","mage"]},"woman-mage":{"a":"Woman Mage","b":"1F9D9-200D-2640-FE0F","j":["sorceress","witch","woman","female","mage"]},"fairy":{"a":"Fairy","b":"1F9DA","j":["Oberon","Puck","Titania","wings","magical"]},"man-fairy":{"a":"Man Fairy","b":"1F9DA-200D-2642-FE0F","j":["Oberon","Puck","man","male"]},"woman-fairy":{"a":"Woman Fairy","b":"1F9DA-200D-2640-FE0F","j":["Titania","woman","female"]},"vampire":{"a":"Vampire","b":"1F9DB","j":["Dracula","undead","blood","twilight"]},"man-vampire":{"a":"Man Vampire","b":"1F9DB-200D-2642-FE0F","j":["Dracula","undead","man","male","dracula"]},"woman-vampire":{"a":"Woman Vampire","b":"1F9DB-200D-2640-FE0F","j":["undead","woman","female"]},"merperson":{"a":"Merperson","b":"1F9DC","j":["mermaid","merman","merwoman","sea"]},"merman":{"a":"Merman","b":"1F9DC-200D-2642-FE0F","j":["Triton","man","male","triton"]},"mermaid":{"a":"Mermaid","b":"1F9DC-200D-2640-FE0F","j":["merwoman","woman","female","ariel"]},"elf":{"a":"Elf","b":"1F9DD","j":["magical","LOTR style"]},"man-elf":{"a":"Man Elf","b":"1F9DD-200D-2642-FE0F","j":["magical","man","male"]},"woman-elf":{"a":"Woman Elf","b":"1F9DD-200D-2640-FE0F","j":["magical","woman","female"]},"genie":{"a":"Genie","b":"1F9DE","j":["djinn","(non-human color)","magical","wishes"]},"man-genie":{"a":"Man Genie","b":"1F9DE-200D-2642-FE0F","j":["djinn","man","male"]},"woman-genie":{"a":"Woman Genie","b":"1F9DE-200D-2640-FE0F","j":["djinn","woman","female"]},"zombie":{"a":"Zombie","b":"1F9DF","j":["undead","walking dead","(non-human color)","dead"]},"man-zombie":{"a":"Man Zombie","b":"1F9DF-200D-2642-FE0F","j":["undead","walking dead","man","male","dracula"]},"woman-zombie":{"a":"Woman Zombie","b":"1F9DF-200D-2640-FE0F","j":["undead","walking dead","woman","female"]},"troll":{"a":"⊛ Troll","b":"1F9CC","j":["fairy tale","fantasy","monster","mystical"]},"person-getting-massage":{"a":"Person Getting Massage","b":"1F486","j":["face","massage","salon","relax"]},"man-getting-massage":{"a":"Man Getting Massage","b":"1F486-200D-2642-FE0F","j":["face","man","massage","male","boy","head"]},"woman-getting-massage":{"a":"Woman Getting Massage","b":"1F486-200D-2640-FE0F","j":["face","massage","woman","female","girl","head"]},"person-getting-haircut":{"a":"Person Getting Haircut","b":"1F487","j":["barber","beauty","haircut","parlor","hairstyle"]},"man-getting-haircut":{"a":"Man Getting Haircut","b":"1F487-200D-2642-FE0F","j":["haircut","man","male","boy"]},"woman-getting-haircut":{"a":"Woman Getting Haircut","b":"1F487-200D-2640-FE0F","j":["haircut","woman","female","girl"]},"person-walking":{"a":"Person Walking","b":"1F6B6","j":["hike","walk","walking","move"]},"man-walking":{"a":"Man Walking","b":"1F6B6-200D-2642-FE0F","j":["hike","man","walk","human","feet","steps"]},"woman-walking":{"a":"Woman Walking","b":"1F6B6-200D-2640-FE0F","j":["hike","walk","woman","human","feet","steps","female"]},"person-standing":{"a":"Person Standing","b":"1F9CD","j":["stand","standing","still"]},"man-standing":{"a":"Man Standing","b":"1F9CD-200D-2642-FE0F","j":["man","standing","still"]},"woman-standing":{"a":"Woman Standing","b":"1F9CD-200D-2640-FE0F","j":["standing","woman","still"]},"person-kneeling":{"a":"Person Kneeling","b":"1F9CE","j":["kneel","kneeling","pray","respectful"]},"man-kneeling":{"a":"Man Kneeling","b":"1F9CE-200D-2642-FE0F","j":["kneeling","man","pray","respectful"]},"woman-kneeling":{"a":"Woman Kneeling","b":"1F9CE-200D-2640-FE0F","j":["kneeling","woman","respectful","pray"]},"person-with-white-cane":{"a":"Person with White Cane","b":"1F9D1-200D-1F9AF","j":["accessibility","blind","person_with_probing_cane"]},"man-with-white-cane":{"a":"Man with White Cane","b":"1F468-200D-1F9AF","j":["accessibility","blind","man","man_with_probing_cane"]},"woman-with-white-cane":{"a":"Woman with White Cane","b":"1F469-200D-1F9AF","j":["accessibility","blind","woman","woman_with_probing_cane"]},"person-in-motorized-wheelchair":{"a":"Person in Motorized Wheelchair","b":"1F9D1-200D-1F9BC","j":["accessibility","wheelchair","disability"]},"man-in-motorized-wheelchair":{"a":"Man in Motorized Wheelchair","b":"1F468-200D-1F9BC","j":["accessibility","man","wheelchair","disability"]},"woman-in-motorized-wheelchair":{"a":"Woman in Motorized Wheelchair","b":"1F469-200D-1F9BC","j":["accessibility","wheelchair","woman","disability"]},"person-in-manual-wheelchair":{"a":"Person in Manual Wheelchair","b":"1F9D1-200D-1F9BD","j":["accessibility","wheelchair","disability"]},"man-in-manual-wheelchair":{"a":"Man in Manual Wheelchair","b":"1F468-200D-1F9BD","j":["accessibility","man","wheelchair","disability"]},"woman-in-manual-wheelchair":{"a":"Woman in Manual Wheelchair","b":"1F469-200D-1F9BD","j":["accessibility","wheelchair","woman","disability"]},"person-running":{"a":"Person Running","b":"1F3C3","j":["marathon","running","move"]},"man-running":{"a":"Man Running","b":"1F3C3-200D-2642-FE0F","j":["man","marathon","racing","running","walking","exercise","race"]},"woman-running":{"a":"Woman Running","b":"1F3C3-200D-2640-FE0F","j":["marathon","racing","running","woman","walking","exercise","race","female"]},"woman-dancing":{"a":"Woman Dancing","b":"1F483","j":["dance","dancing","woman","female","girl","fun"]},"man-dancing":{"a":"Man Dancing","b":"1F57A","j":["dance","dancing","man","male","boy","fun","dancer"]},"person-in-suit-levitating":{"a":"Person in Suit Levitating","b":"1F574","j":["business","person","suit","man_in_suit_levitating","levitate","hover","jump"]},"people-with-bunny-ears":{"a":"People with Bunny Ears","b":"1F46F","j":["bunny ear","dancer","partying","perform","costume"]},"men-with-bunny-ears":{"a":"Men with Bunny Ears","b":"1F46F-200D-2642-FE0F","j":["bunny ear","dancer","men","partying","male","bunny","boys"]},"women-with-bunny-ears":{"a":"Women with Bunny Ears","b":"1F46F-200D-2640-FE0F","j":["bunny ear","dancer","partying","women","female","bunny","girls"]},"person-in-steamy-room":{"a":"Person in Steamy Room","b":"1F9D6","j":["sauna","steam room","hamam","steambath","relax","spa"]},"man-in-steamy-room":{"a":"Man in Steamy Room","b":"1F9D6-200D-2642-FE0F","j":["sauna","steam room","male","man","spa","steamroom"]},"woman-in-steamy-room":{"a":"Woman in Steamy Room","b":"1F9D6-200D-2640-FE0F","j":["sauna","steam room","female","woman","spa","steamroom"]},"person-climbing":{"a":"Person Climbing","b":"1F9D7","j":["climber","sport"]},"man-climbing":{"a":"Man Climbing","b":"1F9D7-200D-2642-FE0F","j":["climber","sports","hobby","man","male","rock"]},"woman-climbing":{"a":"Woman Climbing","b":"1F9D7-200D-2640-FE0F","j":["climber","sports","hobby","woman","female","rock"]},"person-fencing":{"a":"Person Fencing","b":"1F93A","j":["fencer","fencing","sword","sports"]},"horse-racing":{"a":"Horse Racing","b":"1F3C7","j":["horse","jockey","racehorse","racing","animal","betting","competition","gambling","luck"]},"skier":{"a":"Skier","b":"26F7","j":["ski","snow","sports","winter"]},"snowboarder":{"a":"Snowboarder","b":"1F3C2","j":["ski","snow","snowboard","sports","winter"]},"person-golfing":{"a":"Person Golfing","b":"1F3CC","j":["ball","golf","sports","business"]},"man-golfing":{"a":"Man Golfing","b":"1F3CC-FE0F-200D-2642-FE0F","j":["golf","man","sport"]},"woman-golfing":{"a":"Woman Golfing","b":"1F3CC-FE0F-200D-2640-FE0F","j":["golf","woman","sports","business","female"]},"person-surfing":{"a":"Person Surfing","b":"1F3C4","j":["surfing","sport","sea"]},"man-surfing":{"a":"Man Surfing","b":"1F3C4-200D-2642-FE0F","j":["man","surfing","sports","ocean","sea","summer","beach"]},"woman-surfing":{"a":"Woman Surfing","b":"1F3C4-200D-2640-FE0F","j":["surfing","woman","sports","ocean","sea","summer","beach","female"]},"person-rowing-boat":{"a":"Person Rowing Boat","b":"1F6A3","j":["boat","rowboat","sport","move"]},"man-rowing-boat":{"a":"Man Rowing Boat","b":"1F6A3-200D-2642-FE0F","j":["boat","man","rowboat","sports","hobby","water","ship"]},"woman-rowing-boat":{"a":"Woman Rowing Boat","b":"1F6A3-200D-2640-FE0F","j":["boat","rowboat","woman","sports","hobby","water","ship","female"]},"person-swimming":{"a":"Person Swimming","b":"1F3CA","j":["swim","sport","pool"]},"man-swimming":{"a":"Man Swimming","b":"1F3CA-200D-2642-FE0F","j":["man","swim","sports","exercise","human","athlete","water","summer"]},"woman-swimming":{"a":"Woman Swimming","b":"1F3CA-200D-2640-FE0F","j":["swim","woman","sports","exercise","human","athlete","water","summer","female"]},"person-bouncing-ball":{"a":"Person Bouncing Ball","b":"26F9","j":["ball","sports","human"]},"man-bouncing-ball":{"a":"Man Bouncing Ball","b":"26F9-FE0F-200D-2642-FE0F","j":["ball","man","sport"]},"woman-bouncing-ball":{"a":"Woman Bouncing Ball","b":"26F9-FE0F-200D-2640-FE0F","j":["ball","woman","sports","human","female"]},"person-lifting-weights":{"a":"Person Lifting Weights","b":"1F3CB","j":["lifter","weight","sports","training","exercise"]},"man-lifting-weights":{"a":"Man Lifting Weights","b":"1F3CB-FE0F-200D-2642-FE0F","j":["man","weight lifter","sport"]},"woman-lifting-weights":{"a":"Woman Lifting Weights","b":"1F3CB-FE0F-200D-2640-FE0F","j":["weight lifter","woman","sports","training","exercise","female"]},"person-biking":{"a":"Person Biking","b":"1F6B4","j":["bicycle","biking","cyclist","sport","move"]},"man-biking":{"a":"Man Biking","b":"1F6B4-200D-2642-FE0F","j":["bicycle","biking","cyclist","man","sports","bike","exercise","hipster"]},"woman-biking":{"a":"Woman Biking","b":"1F6B4-200D-2640-FE0F","j":["bicycle","biking","cyclist","woman","sports","bike","exercise","hipster","female"]},"person-mountain-biking":{"a":"Person Mountain Biking","b":"1F6B5","j":["bicycle","bicyclist","bike","cyclist","mountain","sport","move"]},"man-mountain-biking":{"a":"Man Mountain Biking","b":"1F6B5-200D-2642-FE0F","j":["bicycle","bike","cyclist","man","mountain","transportation","sports","human","race"]},"woman-mountain-biking":{"a":"Woman Mountain Biking","b":"1F6B5-200D-2640-FE0F","j":["bicycle","bike","biking","cyclist","mountain","woman","transportation","sports","human","race","female"]},"person-cartwheeling":{"a":"Person Cartwheeling","b":"1F938","j":["cartwheel","gymnastics","sport","gymnastic"]},"man-cartwheeling":{"a":"Man Cartwheeling","b":"1F938-200D-2642-FE0F","j":["cartwheel","gymnastics","man"]},"woman-cartwheeling":{"a":"Woman Cartwheeling","b":"1F938-200D-2640-FE0F","j":["cartwheel","gymnastics","woman"]},"people-wrestling":{"a":"People Wrestling","b":"1F93C","j":["wrestle","wrestler","sport"]},"men-wrestling":{"a":"Men Wrestling","b":"1F93C-200D-2642-FE0F","j":["men","wrestle","sports","wrestlers"]},"women-wrestling":{"a":"Women Wrestling","b":"1F93C-200D-2640-FE0F","j":["women","wrestle","sports","wrestlers"]},"person-playing-water-polo":{"a":"Person Playing Water Polo","b":"1F93D","j":["polo","water","sport"]},"man-playing-water-polo":{"a":"Man Playing Water Polo","b":"1F93D-200D-2642-FE0F","j":["man","water polo","sports","pool"]},"woman-playing-water-polo":{"a":"Woman Playing Water Polo","b":"1F93D-200D-2640-FE0F","j":["water polo","woman","sports","pool"]},"person-playing-handball":{"a":"Person Playing Handball","b":"1F93E","j":["ball","handball","sport"]},"man-playing-handball":{"a":"Man Playing Handball","b":"1F93E-200D-2642-FE0F","j":["handball","man","sports"]},"woman-playing-handball":{"a":"Woman Playing Handball","b":"1F93E-200D-2640-FE0F","j":["handball","woman","sports"]},"person-juggling":{"a":"Person Juggling","b":"1F939","j":["balance","juggle","multitask","skill","performance"]},"man-juggling":{"a":"Man Juggling","b":"1F939-200D-2642-FE0F","j":["juggling","man","multitask","juggle","balance","skill"]},"woman-juggling":{"a":"Woman Juggling","b":"1F939-200D-2640-FE0F","j":["juggling","multitask","woman","juggle","balance","skill"]},"person-in-lotus-position":{"a":"Person in Lotus Position","b":"1F9D8","j":["meditation","yoga","serenity","meditate"]},"man-in-lotus-position":{"a":"Man in Lotus Position","b":"1F9D8-200D-2642-FE0F","j":["meditation","yoga","man","male","serenity","zen","mindfulness"]},"woman-in-lotus-position":{"a":"Woman in Lotus Position","b":"1F9D8-200D-2640-FE0F","j":["meditation","yoga","woman","female","serenity","zen","mindfulness"]},"person-taking-bath":{"a":"Person Taking Bath","b":"1F6C0","j":["bath","bathtub","clean","shower","bathroom"]},"person-in-bed":{"a":"Person in Bed","b":"1F6CC","j":["hotel","sleep","bed","rest"]},"people-holding-hands":{"a":"People Holding Hands","b":"1F9D1-200D-1F91D-200D-1F9D1","j":["couple","hand","hold","holding hands","person","friendship"]},"women-holding-hands":{"a":"Women Holding Hands","b":"1F46D","j":["couple","hand","holding hands","women","pair","friendship","love","like","female","people","human"]},"woman-and-man-holding-hands":{"a":"Woman and Man Holding Hands","b":"1F46B","j":["couple","hand","hold","holding hands","man","woman","pair","people","human","love","date","dating","like","affection","valentines","marriage"]},"men-holding-hands":{"a":"Men Holding Hands","b":"1F46C","j":["couple","Gemini","holding hands","man","men","twins","zodiac","pair","love","like","bromance","friendship","people","human"]},"kiss":{"a":"Kiss","b":"1F48F","j":["couple","pair","valentines","love","like","dating","marriage"]},"kiss-woman-man":{"a":"Kiss: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","woman","love"]},"kiss-man-man":{"a":"Kiss: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","pair","valentines","love","like","dating","marriage"]},"kiss-woman-woman":{"a":"Kiss: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","j":["couple","kiss","woman","pair","valentines","love","like","dating","marriage"]},"couple-with-heart":{"a":"Couple with Heart","b":"1F491","j":["couple","love","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-man":{"a":"Couple with Heart: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","woman"]},"couple-with-heart-man-man":{"a":"Couple with Heart: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-woman":{"a":"Couple with Heart: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F469","j":["couple","couple with heart","love","woman","pair","like","affection","human","dating","valentines","marriage"]},"family":{"a":"Family","b":"1F46A","j":["home","parents","child","mom","dad","father","mother","people","human"]},"family-man-woman-boy":{"a":"Family: Man, Woman, Boy","b":"1F468-200D-1F469-200D-1F466","j":["boy","family","man","woman","love"]},"family-man-woman-girl":{"a":"Family: Man, Woman, Girl","b":"1F468-200D-1F469-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","child"]},"family-man-woman-girl-boy":{"a":"Family: Man, Woman, Girl, Boy","b":"1F468-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","man","woman","home","parents","people","human","children"]},"family-man-woman-boy-boy":{"a":"Family: Man, Woman, Boy, Boy","b":"1F468-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","man","woman","home","parents","people","human","children"]},"family-man-woman-girl-girl":{"a":"Family: Man, Woman, Girl, Girl","b":"1F468-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","children"]},"family-man-man-boy":{"a":"Family: Man, Man, Boy","b":"1F468-200D-1F468-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl":{"a":"Family: Man, Man, Girl","b":"1F468-200D-1F468-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-man-man-girl-boy":{"a":"Family: Man, Man, Girl, Boy","b":"1F468-200D-1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parents","people","human","children"]},"family-man-man-boy-boy":{"a":"Family: Man, Man, Boy, Boy","b":"1F468-200D-1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl-girl":{"a":"Family: Man, Man, Girl, Girl","b":"1F468-200D-1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-woman-woman-boy":{"a":"Family: Woman, Woman, Boy","b":"1F469-200D-1F469-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl":{"a":"Family: Woman, Woman, Girl","b":"1F469-200D-1F469-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-girl-boy":{"a":"Family: Woman, Woman, Girl, Boy","b":"1F469-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-boy-boy":{"a":"Family: Woman, Woman, Boy, Boy","b":"1F469-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl-girl":{"a":"Family: Woman, Woman, Girl, Girl","b":"1F469-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-man-boy":{"a":"Family: Man, Boy","b":"1F468-200D-1F466","j":["boy","family","man","home","parent","people","human","child"]},"family-man-boy-boy":{"a":"Family: Man, Boy, Boy","b":"1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parent","people","human","children"]},"family-man-girl":{"a":"Family: Man, Girl","b":"1F468-200D-1F467","j":["family","girl","man","home","parent","people","human","child"]},"family-man-girl-boy":{"a":"Family: Man, Girl, Boy","b":"1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parent","people","human","children"]},"family-man-girl-girl":{"a":"Family: Man, Girl, Girl","b":"1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parent","people","human","children"]},"family-woman-boy":{"a":"Family: Woman, Boy","b":"1F469-200D-1F466","j":["boy","family","woman","home","parent","people","human","child"]},"family-woman-boy-boy":{"a":"Family: Woman, Boy, Boy","b":"1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parent","people","human","children"]},"family-woman-girl":{"a":"Family: Woman, Girl","b":"1F469-200D-1F467","j":["family","girl","woman","home","parent","people","human","child"]},"family-woman-girl-boy":{"a":"Family: Woman, Girl, Boy","b":"1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parent","people","human","children"]},"family-woman-girl-girl":{"a":"Family: Woman, Girl, Girl","b":"1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parent","people","human","children"]},"speaking-head":{"a":"Speaking Head","b":"1F5E3","j":["face","head","silhouette","speak","speaking","user","person","human","sing","say","talk"]},"bust-in-silhouette":{"a":"Bust in Silhouette","b":"1F464","j":["bust","silhouette","user","person","human"]},"busts-in-silhouette":{"a":"Busts in Silhouette","b":"1F465","j":["bust","silhouette","user","person","human","group","team"]},"people-hugging":{"a":"People Hugging","b":"1FAC2","j":["goodbye","hello","hug","thanks","care"]},"footprints":{"a":"Footprints","b":"1F463","j":["clothing","footprint","print","feet","tracking","walking","beach"]},"red-hair":{"a":"Red Hair","b":"1F9B0","j":["ginger","red hair","redhead"]},"curly-hair":{"a":"Curly Hair","b":"1F9B1","j":["afro","curly","curly hair","ringlets"]},"white-hair":{"a":"White Hair","b":"1F9B3","j":["gray","hair","old","white"]},"bald":{"a":"Bald","b":"1F9B2","j":["bald","chemotherapy","hairless","no hair","shaven"]},"monkey-face":{"a":"Monkey Face","b":"1F435","j":["face","monkey","animal","nature","circus"]},"monkey":{"a":"Monkey","b":"1F412","j":["animal","nature","banana","circus"]},"gorilla":{"a":"Gorilla","b":"1F98D","j":["animal","nature","circus"]},"orangutan":{"a":"Orangutan","b":"1F9A7","j":["ape","animal"]},"dog-face":{"a":"Dog Face","b":"1F436","j":["dog","face","pet","animal","friend","nature","woof","puppy","faithful"]},"dog":{"a":"Dog","b":"1F415","j":["pet","animal","nature","friend","doge","faithful"]},"guide-dog":{"a":"Guide Dog","b":"1F9AE","j":["accessibility","blind","guide","animal"]},"service-dog":{"a":"Service Dog","b":"1F415-200D-1F9BA","j":["accessibility","assistance","dog","service","blind","animal"]},"poodle":{"a":"Poodle","b":"1F429","j":["dog","animal","101","nature","pet"]},"wolf":{"a":"Wolf","b":"1F43A","j":["face","animal","nature","wild"]},"fox":{"a":"Fox","b":"1F98A","j":["face","animal","nature"]},"raccoon":{"a":"Raccoon","b":"1F99D","j":["curious","sly","animal","nature"]},"cat-face":{"a":"Cat Face","b":"1F431","j":["cat","face","pet","animal","meow","nature","kitten"]},"cat":{"a":"Cat","b":"1F408","j":["pet","animal","meow","cats"]},"black-cat":{"a":"Black Cat","b":"1F408-200D-2B1B","j":["black","cat","unlucky","superstition","luck"]},"lion":{"a":"Lion","b":"1F981","j":["face","Leo","zodiac","animal","nature"]},"tiger-face":{"a":"Tiger Face","b":"1F42F","j":["face","tiger","animal","cat","danger","wild","nature","roar"]},"tiger":{"a":"Tiger","b":"1F405","j":["animal","nature","roar"]},"leopard":{"a":"Leopard","b":"1F406","j":["animal","nature"]},"horse-face":{"a":"Horse Face","b":"1F434","j":["face","horse","animal","brown","nature"]},"horse":{"a":"Horse","b":"1F40E","j":["equestrian","racehorse","racing","animal","gamble","luck"]},"unicorn":{"a":"Unicorn","b":"1F984","j":["face","animal","nature","mystical"]},"zebra":{"a":"Zebra","b":"1F993","j":["stripe","animal","nature","stripes","safari"]},"deer":{"a":"Deer","b":"1F98C","j":["animal","nature","horns","venison"]},"bison":{"a":"Bison","b":"1F9AC","j":["buffalo","herd","wisent","ox"]},"cow-face":{"a":"Cow Face","b":"1F42E","j":["cow","face","beef","ox","animal","nature","moo","milk"]},"ox":{"a":"Ox","b":"1F402","j":["bull","Taurus","zodiac","animal","cow","beef"]},"water-buffalo":{"a":"Water Buffalo","b":"1F403","j":["buffalo","water","animal","nature","ox","cow"]},"cow":{"a":"Cow","b":"1F404","j":["beef","ox","animal","nature","moo","milk"]},"pig-face":{"a":"Pig Face","b":"1F437","j":["face","pig","animal","oink","nature"]},"pig":{"a":"Pig","b":"1F416","j":["sow","animal","nature"]},"boar":{"a":"Boar","b":"1F417","j":["pig","animal","nature"]},"pig-nose":{"a":"Pig Nose","b":"1F43D","j":["face","nose","pig","animal","oink"]},"ram":{"a":"Ram","b":"1F40F","j":["Aries","male","sheep","zodiac","animal","nature"]},"ewe":{"a":"Ewe","b":"1F411","j":["female","sheep","animal","nature","wool","shipit"]},"goat":{"a":"Goat","b":"1F410","j":["Capricorn","zodiac","animal","nature"]},"camel":{"a":"Camel","b":"1F42A","j":["dromedary","hump","animal","hot","desert"]},"twohump-camel":{"a":"Two-Hump Camel","b":"1F42B","j":["bactrian","camel","hump","two-hump camel","two_hump_camel","animal","nature","hot","desert"]},"llama":{"a":"Llama","b":"1F999","j":["alpaca","guanaco","vicuña","wool","animal","nature"]},"giraffe":{"a":"Giraffe","b":"1F992","j":["spots","animal","nature","safari"]},"elephant":{"a":"Elephant","b":"1F418","j":["animal","nature","nose","th","circus"]},"mammoth":{"a":"Mammoth","b":"1F9A3","j":["extinction","large","tusk","woolly","elephant","tusks"]},"rhinoceros":{"a":"Rhinoceros","b":"1F98F","j":["animal","nature","horn"]},"hippopotamus":{"a":"Hippopotamus","b":"1F99B","j":["hippo","animal","nature"]},"mouse-face":{"a":"Mouse Face","b":"1F42D","j":["face","mouse","animal","nature","cheese_wedge","rodent"]},"mouse":{"a":"Mouse","b":"1F401","j":["animal","nature","rodent"]},"rat":{"a":"Rat","b":"1F400","j":["animal","mouse","rodent"]},"hamster":{"a":"Hamster","b":"1F439","j":["face","pet","animal","nature"]},"rabbit-face":{"a":"Rabbit Face","b":"1F430","j":["bunny","face","pet","rabbit","animal","nature","spring","magic"]},"rabbit":{"a":"Rabbit","b":"1F407","j":["bunny","pet","animal","nature","magic","spring"]},"chipmunk":{"a":"Chipmunk","b":"1F43F","j":["squirrel","animal","nature","rodent"]},"beaver":{"a":"Beaver","b":"1F9AB","j":["dam","animal","rodent"]},"hedgehog":{"a":"Hedgehog","b":"1F994","j":["spiny","animal","nature"]},"bat":{"a":"Bat","b":"1F987","j":["vampire","animal","nature","blind"]},"bear":{"a":"Bear","b":"1F43B","j":["face","animal","nature","wild"]},"polar-bear":{"a":"Polar Bear","b":"1F43B-200D-2744-FE0F","j":["arctic","bear","white","animal"]},"koala":{"a":"Koala","b":"1F428","j":["face","marsupial","animal","nature"]},"panda":{"a":"Panda","b":"1F43C","j":["face","animal","nature"]},"sloth":{"a":"Sloth","b":"1F9A5","j":["lazy","slow","animal"]},"otter":{"a":"Otter","b":"1F9A6","j":["fishing","playful","animal"]},"skunk":{"a":"Skunk","b":"1F9A8","j":["stink","animal"]},"kangaroo":{"a":"Kangaroo","b":"1F998","j":["Australia","joey","jump","marsupial","animal","nature","australia","hop"]},"badger":{"a":"Badger","b":"1F9A1","j":["honey badger","pester","animal","nature","honey"]},"paw-prints":{"a":"Paw Prints","b":"1F43E","j":["feet","paw","print","animal","tracking","footprints","dog","cat","pet"]},"turkey":{"a":"Turkey","b":"1F983","j":["bird","animal"]},"chicken":{"a":"Chicken","b":"1F414","j":["bird","animal","cluck","nature"]},"rooster":{"a":"Rooster","b":"1F413","j":["bird","animal","nature","chicken"]},"hatching-chick":{"a":"Hatching Chick","b":"1F423","j":["baby","bird","chick","hatching","animal","chicken","egg","born"]},"baby-chick":{"a":"Baby Chick","b":"1F424","j":["baby","bird","chick","animal","chicken"]},"frontfacing-baby-chick":{"a":"Front-Facing Baby Chick","b":"1F425","j":["baby","bird","chick","front-facing baby chick","front_facing_baby_chick","animal","chicken"]},"bird":{"a":"Bird","b":"1F426","j":["animal","nature","fly","tweet","spring"]},"penguin":{"a":"Penguin","b":"1F427","j":["bird","animal","nature"]},"dove":{"a":"Dove","b":"1F54A","j":["bird","fly","peace","animal"]},"eagle":{"a":"Eagle","b":"1F985","j":["bird","animal","nature"]},"duck":{"a":"Duck","b":"1F986","j":["bird","animal","nature","mallard"]},"swan":{"a":"Swan","b":"1F9A2","j":["bird","cygnet","ugly duckling","animal","nature"]},"owl":{"a":"Owl","b":"1F989","j":["bird","wise","animal","nature","hoot"]},"dodo":{"a":"Dodo","b":"1F9A4","j":["extinction","large","Mauritius","animal","bird"]},"feather":{"a":"Feather","b":"1FAB6","j":["bird","flight","light","plumage","fly"]},"flamingo":{"a":"Flamingo","b":"1F9A9","j":["flamboyant","tropical","animal"]},"peacock":{"a":"Peacock","b":"1F99A","j":["bird","ostentatious","peahen","proud","animal","nature"]},"parrot":{"a":"Parrot","b":"1F99C","j":["bird","pirate","talk","animal","nature"]},"frog":{"a":"Frog","b":"1F438","j":["face","animal","nature","croak","toad"]},"crocodile":{"a":"Crocodile","b":"1F40A","j":["animal","nature","reptile","lizard","alligator"]},"turtle":{"a":"Turtle","b":"1F422","j":["terrapin","tortoise","animal","slow","nature"]},"lizard":{"a":"Lizard","b":"1F98E","j":["reptile","animal","nature"]},"snake":{"a":"Snake","b":"1F40D","j":["bearer","Ophiuchus","serpent","zodiac","animal","evil","nature","hiss","python"]},"dragon-face":{"a":"Dragon Face","b":"1F432","j":["dragon","face","fairy tale","animal","myth","nature","chinese","green"]},"dragon":{"a":"Dragon","b":"1F409","j":["fairy tale","animal","myth","nature","chinese","green"]},"sauropod":{"a":"Sauropod","b":"1F995","j":["brachiosaurus","brontosaurus","diplodocus","animal","nature","dinosaur","extinct"]},"trex":{"a":"T-Rex","b":"1F996","j":["Tyrannosaurus Rex","t_rex","animal","nature","dinosaur","tyrannosaurus","extinct"]},"spouting-whale":{"a":"Spouting Whale","b":"1F433","j":["face","spouting","whale","animal","nature","sea","ocean"]},"whale":{"a":"Whale","b":"1F40B","j":["animal","nature","sea","ocean"]},"dolphin":{"a":"Dolphin","b":"1F42C","j":["flipper","animal","nature","fish","sea","ocean","fins","beach"]},"seal":{"a":"Seal","b":"1F9AD","j":["sea lion","animal","creature","sea"]},"fish":{"a":"Fish","b":"1F41F","j":["Pisces","zodiac","animal","food","nature"]},"tropical-fish":{"a":"Tropical Fish","b":"1F420","j":["fish","tropical","animal","swim","ocean","beach","nemo"]},"blowfish":{"a":"Blowfish","b":"1F421","j":["fish","animal","nature","food","sea","ocean"]},"shark":{"a":"Shark","b":"1F988","j":["fish","animal","nature","sea","ocean","jaws","fins","beach"]},"octopus":{"a":"Octopus","b":"1F419","j":["animal","creature","ocean","sea","nature","beach"]},"spiral-shell":{"a":"Spiral Shell","b":"1F41A","j":["shell","spiral","nature","sea","beach"]},"coral":{"a":"⊛ Coral","b":"1FAB8","j":["ocean","reef","sea"]},"snail":{"a":"Snail","b":"1F40C","j":["slow","animal","shell"]},"butterfly":{"a":"Butterfly","b":"1F98B","j":["insect","pretty","animal","nature","caterpillar"]},"bug":{"a":"Bug","b":"1F41B","j":["insect","animal","nature","worm"]},"ant":{"a":"Ant","b":"1F41C","j":["insect","animal","nature","bug"]},"honeybee":{"a":"Honeybee","b":"1F41D","j":["bee","insect","animal","nature","bug","spring","honey"]},"beetle":{"a":"Beetle","b":"1FAB2","j":["bug","insect"]},"lady-beetle":{"a":"Lady Beetle","b":"1F41E","j":["beetle","insect","ladybird","ladybug","animal","nature"]},"cricket":{"a":"Cricket","b":"1F997","j":["grasshopper","Orthoptera","animal","chirp"]},"cockroach":{"a":"Cockroach","b":"1FAB3","j":["insect","pest","roach","pests"]},"spider":{"a":"Spider","b":"1F577","j":["insect","animal","arachnid"]},"spider-web":{"a":"Spider Web","b":"1F578","j":["spider","web","animal","insect","arachnid","silk"]},"scorpion":{"a":"Scorpion","b":"1F982","j":["scorpio","Scorpio","zodiac","animal","arachnid"]},"mosquito":{"a":"Mosquito","b":"1F99F","j":["disease","fever","malaria","pest","virus","animal","nature","insect"]},"fly":{"a":"Fly","b":"1FAB0","j":["disease","maggot","pest","rotting","insect"]},"worm":{"a":"Worm","b":"1FAB1","j":["annelid","earthworm","parasite","animal"]},"microbe":{"a":"Microbe","b":"1F9A0","j":["amoeba","bacteria","virus","germs","covid"]},"bouquet":{"a":"Bouquet","b":"1F490","j":["flower","flowers","nature","spring"]},"cherry-blossom":{"a":"Cherry Blossom","b":"1F338","j":["blossom","cherry","flower","nature","plant","spring"]},"white-flower":{"a":"White Flower","b":"1F4AE","j":["flower","japanese","spring"]},"lotus":{"a":"⊛ Lotus","b":"1FAB7","j":["Buddhism","flower","Hinduism","India","purity","Vietnam","calm","meditation"]},"rosette":{"a":"Rosette","b":"1F3F5","j":["plant","flower","decoration","military"]},"rose":{"a":"Rose","b":"1F339","j":["flower","flowers","valentines","love","spring"]},"wilted-flower":{"a":"Wilted Flower","b":"1F940","j":["flower","wilted","plant","nature"]},"hibiscus":{"a":"Hibiscus","b":"1F33A","j":["flower","plant","vegetable","flowers","beach"]},"sunflower":{"a":"Sunflower","b":"1F33B","j":["flower","sun","nature","plant","fall"]},"blossom":{"a":"Blossom","b":"1F33C","j":["flower","nature","flowers","yellow"]},"tulip":{"a":"Tulip","b":"1F337","j":["flower","flowers","plant","nature","summer","spring"]},"seedling":{"a":"Seedling","b":"1F331","j":["young","plant","nature","grass","lawn","spring"]},"potted-plant":{"a":"Potted Plant","b":"1FAB4","j":["boring","grow","house","nurturing","plant","useless","greenery"]},"evergreen-tree":{"a":"Evergreen Tree","b":"1F332","j":["tree","plant","nature"]},"deciduous-tree":{"a":"Deciduous Tree","b":"1F333","j":["deciduous","shedding","tree","plant","nature"]},"palm-tree":{"a":"Palm Tree","b":"1F334","j":["palm","tree","plant","vegetable","nature","summer","beach","mojito","tropical"]},"cactus":{"a":"Cactus","b":"1F335","j":["plant","vegetable","nature"]},"sheaf-of-rice":{"a":"Sheaf of Rice","b":"1F33E","j":["ear","grain","rice","nature","plant"]},"herb":{"a":"Herb","b":"1F33F","j":["leaf","vegetable","plant","medicine","weed","grass","lawn"]},"shamrock":{"a":"Shamrock","b":"2618","j":["plant","vegetable","nature","irish","clover"]},"four-leaf-clover":{"a":"Four Leaf Clover","b":"1F340","j":["4","clover","four","four-leaf clover","leaf","vegetable","plant","nature","lucky","irish"]},"maple-leaf":{"a":"Maple Leaf","b":"1F341","j":["falling","leaf","maple","nature","plant","vegetable","ca","fall"]},"fallen-leaf":{"a":"Fallen Leaf","b":"1F342","j":["falling","leaf","nature","plant","vegetable","leaves"]},"leaf-fluttering-in-wind":{"a":"Leaf Fluttering in Wind","b":"1F343","j":["blow","flutter","leaf","wind","nature","plant","tree","vegetable","grass","lawn","spring"]},"empty-nest":{"a":"⊛ Empty Nest","b":"1FAB9","j":["nesting","bird"]},"nest-with-eggs":{"a":"⊛ Nest with Eggs","b":"1FABA","j":["nesting","bird"]},"grapes":{"a":"Grapes","b":"1F347","j":["fruit","grape","food","wine"]},"melon":{"a":"Melon","b":"1F348","j":["fruit","nature","food"]},"watermelon":{"a":"Watermelon","b":"1F349","j":["fruit","food","picnic","summer"]},"tangerine":{"a":"Tangerine","b":"1F34A","j":["fruit","orange","food","nature"]},"lemon":{"a":"Lemon","b":"1F34B","j":["citrus","fruit","nature"]},"banana":{"a":"Banana","b":"1F34C","j":["fruit","food","monkey"]},"pineapple":{"a":"Pineapple","b":"1F34D","j":["fruit","nature","food"]},"mango":{"a":"Mango","b":"1F96D","j":["fruit","tropical","food"]},"red-apple":{"a":"Red Apple","b":"1F34E","j":["apple","fruit","red","mac","school"]},"green-apple":{"a":"Green Apple","b":"1F34F","j":["apple","fruit","green","nature"]},"pear":{"a":"Pear","b":"1F350","j":["fruit","nature","food"]},"peach":{"a":"Peach","b":"1F351","j":["fruit","nature","food"]},"cherries":{"a":"Cherries","b":"1F352","j":["berries","cherry","fruit","red","food"]},"strawberry":{"a":"Strawberry","b":"1F353","j":["berry","fruit","food","nature"]},"blueberries":{"a":"Blueberries","b":"1FAD0","j":["berry","bilberry","blue","blueberry","fruit"]},"kiwi-fruit":{"a":"Kiwi Fruit","b":"1F95D","j":["food","fruit","kiwi"]},"tomato":{"a":"Tomato","b":"1F345","j":["fruit","vegetable","nature","food"]},"olive":{"a":"Olive","b":"1FAD2","j":["food","fruit"]},"coconut":{"a":"Coconut","b":"1F965","j":["palm","piña colada","fruit","nature","food"]},"avocado":{"a":"Avocado","b":"1F951","j":["food","fruit"]},"eggplant":{"a":"Eggplant","b":"1F346","j":["aubergine","vegetable","nature","food"]},"potato":{"a":"Potato","b":"1F954","j":["food","vegetable","tuber","vegatable","starch"]},"carrot":{"a":"Carrot","b":"1F955","j":["food","vegetable","orange"]},"ear-of-corn":{"a":"Ear of Corn","b":"1F33D","j":["corn","ear","maize","maze","food","vegetable","plant"]},"hot-pepper":{"a":"Hot Pepper","b":"1F336","j":["hot","pepper","food","spicy","chilli","chili"]},"bell-pepper":{"a":"Bell Pepper","b":"1FAD1","j":["capsicum","pepper","vegetable","fruit","plant"]},"cucumber":{"a":"Cucumber","b":"1F952","j":["food","pickle","vegetable","fruit"]},"leafy-green":{"a":"Leafy Green","b":"1F96C","j":["bok choy","cabbage","kale","lettuce","food","vegetable","plant"]},"broccoli":{"a":"Broccoli","b":"1F966","j":["wild cabbage","fruit","food","vegetable"]},"garlic":{"a":"Garlic","b":"1F9C4","j":["flavoring","food","spice","cook"]},"onion":{"a":"Onion","b":"1F9C5","j":["flavoring","cook","food","spice"]},"mushroom":{"a":"Mushroom","b":"1F344","j":["toadstool","plant","vegetable"]},"peanuts":{"a":"Peanuts","b":"1F95C","j":["food","nut","peanut","vegetable"]},"beans":{"a":"⊛ Beans","b":"1FAD8","j":["food","kidney","legume"]},"chestnut":{"a":"Chestnut","b":"1F330","j":["plant","food","squirrel"]},"bread":{"a":"Bread","b":"1F35E","j":["loaf","food","wheat","breakfast","toast"]},"croissant":{"a":"Croissant","b":"1F950","j":["bread","breakfast","food","french","roll"]},"baguette-bread":{"a":"Baguette Bread","b":"1F956","j":["baguette","bread","food","french","france","bakery"]},"flatbread":{"a":"Flatbread","b":"1FAD3","j":["arepa","lavash","naan","pita","flour","food","bakery"]},"pretzel":{"a":"Pretzel","b":"1F968","j":["twisted","convoluted","food","bread","germany","bakery"]},"bagel":{"a":"Bagel","b":"1F96F","j":["bakery","breakfast","schmear","food","bread","jewish"]},"pancakes":{"a":"Pancakes","b":"1F95E","j":["breakfast","crêpe","food","hotcake","pancake","flapjacks","hotcakes","brunch"]},"waffle":{"a":"Waffle","b":"1F9C7","j":["breakfast","indecisive","iron","food","brunch"]},"cheese-wedge":{"a":"Cheese Wedge","b":"1F9C0","j":["cheese","food","chadder","swiss"]},"meat-on-bone":{"a":"Meat on Bone","b":"1F356","j":["bone","meat","good","food","drumstick"]},"poultry-leg":{"a":"Poultry Leg","b":"1F357","j":["bone","chicken","drumstick","leg","poultry","food","meat","bird","turkey"]},"cut-of-meat":{"a":"Cut of Meat","b":"1F969","j":["chop","lambchop","porkchop","steak","food","cow","meat","cut"]},"bacon":{"a":"Bacon","b":"1F953","j":["breakfast","food","meat","pork","pig","brunch"]},"hamburger":{"a":"Hamburger","b":"1F354","j":["burger","meat","fast food","beef","cheeseburger","mcdonalds","burger king"]},"french-fries":{"a":"French Fries","b":"1F35F","j":["french","fries","chips","snack","fast food","potato"]},"pizza":{"a":"Pizza","b":"1F355","j":["cheese","slice","food","party","italy"]},"hot-dog":{"a":"Hot Dog","b":"1F32D","j":["frankfurter","hotdog","sausage","food","america"]},"sandwich":{"a":"Sandwich","b":"1F96A","j":["bread","food","lunch","toast","bakery"]},"taco":{"a":"Taco","b":"1F32E","j":["mexican","food"]},"burrito":{"a":"Burrito","b":"1F32F","j":["mexican","wrap","food"]},"tamale":{"a":"Tamale","b":"1FAD4","j":["mexican","wrapped","food","masa"]},"stuffed-flatbread":{"a":"Stuffed Flatbread","b":"1F959","j":["falafel","flatbread","food","gyro","kebab","stuffed","mediterranean"]},"falafel":{"a":"Falafel","b":"1F9C6","j":["chickpea","meatball","food","mediterranean"]},"egg":{"a":"Egg","b":"1F95A","j":["breakfast","food","chicken"]},"cooking":{"a":"Cooking","b":"1F373","j":["breakfast","egg","frying","pan","food","kitchen","skillet"]},"shallow-pan-of-food":{"a":"Shallow Pan of Food","b":"1F958","j":["casserole","food","paella","pan","shallow","cooking","skillet"]},"pot-of-food":{"a":"Pot of Food","b":"1F372","j":["pot","stew","food","meat","soup","hot pot"]},"fondue":{"a":"Fondue","b":"1FAD5","j":["cheese","chocolate","melted","pot","Swiss","food"]},"bowl-with-spoon":{"a":"Bowl with Spoon","b":"1F963","j":["breakfast","cereal","congee","oatmeal","porridge","food"]},"green-salad":{"a":"Green Salad","b":"1F957","j":["food","green","salad","healthy","lettuce","vegetable"]},"popcorn":{"a":"Popcorn","b":"1F37F","j":["food","movie theater","films","snack","drama"]},"butter":{"a":"Butter","b":"1F9C8","j":["dairy","food","cook"]},"salt":{"a":"Salt","b":"1F9C2","j":["condiment","shaker"]},"canned-food":{"a":"Canned Food","b":"1F96B","j":["can","food","soup","tomatoes"]},"bento-box":{"a":"Bento Box","b":"1F371","j":["bento","box","food","japanese","lunch"]},"rice-cracker":{"a":"Rice Cracker","b":"1F358","j":["cracker","rice","food","japanese","snack"]},"rice-ball":{"a":"Rice Ball","b":"1F359","j":["ball","Japanese","rice","food","japanese"]},"cooked-rice":{"a":"Cooked Rice","b":"1F35A","j":["cooked","rice","food","asian"]},"curry-rice":{"a":"Curry Rice","b":"1F35B","j":["curry","rice","food","spicy","hot","indian"]},"steaming-bowl":{"a":"Steaming Bowl","b":"1F35C","j":["bowl","noodle","ramen","steaming","food","japanese","chopsticks"]},"spaghetti":{"a":"Spaghetti","b":"1F35D","j":["pasta","food","italian","noodle"]},"roasted-sweet-potato":{"a":"Roasted Sweet Potato","b":"1F360","j":["potato","roasted","sweet","food","nature","plant"]},"oden":{"a":"Oden","b":"1F362","j":["kebab","seafood","skewer","stick","food","japanese"]},"sushi":{"a":"Sushi","b":"1F363","j":["food","fish","japanese","rice"]},"fried-shrimp":{"a":"Fried Shrimp","b":"1F364","j":["fried","prawn","shrimp","tempura","food","animal","appetizer","summer"]},"fish-cake-with-swirl":{"a":"Fish Cake with Swirl","b":"1F365","j":["cake","fish","pastry","swirl","food","japan","sea","beach","narutomaki","pink","kamaboko","surimi","ramen"]},"moon-cake":{"a":"Moon Cake","b":"1F96E","j":["autumn","festival","yuèbǐng","food","dessert"]},"dango":{"a":"Dango","b":"1F361","j":["dessert","Japanese","skewer","stick","sweet","food","japanese","barbecue","meat"]},"dumpling":{"a":"Dumpling","b":"1F95F","j":["empanada","gyōza","jiaozi","pierogi","potsticker","food","gyoza"]},"fortune-cookie":{"a":"Fortune Cookie","b":"1F960","j":["prophecy","food","dessert"]},"takeout-box":{"a":"Takeout Box","b":"1F961","j":["oyster pail","food","leftovers"]},"crab":{"a":"Crab","b":"1F980","j":["Cancer","zodiac","animal","crustacean"]},"lobster":{"a":"Lobster","b":"1F99E","j":["bisque","claws","seafood","animal","nature"]},"shrimp":{"a":"Shrimp","b":"1F990","j":["food","shellfish","small","animal","ocean","nature","seafood"]},"squid":{"a":"Squid","b":"1F991","j":["food","molusc","animal","nature","ocean","sea"]},"oyster":{"a":"Oyster","b":"1F9AA","j":["diving","pearl","food"]},"soft-ice-cream":{"a":"Soft Ice Cream","b":"1F366","j":["cream","dessert","ice","icecream","soft","sweet","food","hot","summer"]},"shaved-ice":{"a":"Shaved Ice","b":"1F367","j":["dessert","ice","shaved","sweet","hot","summer"]},"ice-cream":{"a":"Ice Cream","b":"1F368","j":["cream","dessert","ice","sweet","food","hot"]},"doughnut":{"a":"Doughnut","b":"1F369","j":["breakfast","dessert","donut","sweet","food","snack"]},"cookie":{"a":"Cookie","b":"1F36A","j":["dessert","sweet","food","snack","oreo","chocolate"]},"birthday-cake":{"a":"Birthday Cake","b":"1F382","j":["birthday","cake","celebration","dessert","pastry","sweet","food"]},"shortcake":{"a":"Shortcake","b":"1F370","j":["cake","dessert","pastry","slice","sweet","food"]},"cupcake":{"a":"Cupcake","b":"1F9C1","j":["bakery","sweet","food","dessert"]},"pie":{"a":"Pie","b":"1F967","j":["filling","pastry","fruit","meat","food","dessert"]},"chocolate-bar":{"a":"Chocolate Bar","b":"1F36B","j":["bar","chocolate","dessert","sweet","food","snack"]},"candy":{"a":"Candy","b":"1F36C","j":["dessert","sweet","snack","lolly"]},"lollipop":{"a":"Lollipop","b":"1F36D","j":["candy","dessert","sweet","food","snack"]},"custard":{"a":"Custard","b":"1F36E","j":["dessert","pudding","sweet","food"]},"honey-pot":{"a":"Honey Pot","b":"1F36F","j":["honey","honeypot","pot","sweet","bees","kitchen"]},"baby-bottle":{"a":"Baby Bottle","b":"1F37C","j":["baby","bottle","drink","milk","food","container"]},"glass-of-milk":{"a":"Glass of Milk","b":"1F95B","j":["drink","glass","milk","beverage","cow"]},"hot-beverage":{"a":"Hot Beverage","b":"2615","j":["beverage","coffee","drink","hot","steaming","tea","caffeine","latte","espresso"]},"teapot":{"a":"Teapot","b":"1FAD6","j":["drink","pot","tea","hot"]},"teacup-without-handle":{"a":"Teacup Without Handle","b":"1F375","j":["beverage","cup","drink","tea","teacup","bowl","breakfast","green","british"]},"sake":{"a":"Sake","b":"1F376","j":["bar","beverage","bottle","cup","drink","wine","drunk","japanese","alcohol","booze"]},"bottle-with-popping-cork":{"a":"Bottle with Popping Cork","b":"1F37E","j":["bar","bottle","cork","drink","popping","wine","celebration"]},"wine-glass":{"a":"Wine Glass","b":"1F377","j":["bar","beverage","drink","glass","wine","drunk","alcohol","booze"]},"cocktail-glass":{"a":"Cocktail Glass","b":"1F378","j":["bar","cocktail","drink","glass","drunk","alcohol","beverage","booze","mojito"]},"tropical-drink":{"a":"Tropical Drink","b":"1F379","j":["bar","drink","tropical","beverage","cocktail","summer","beach","alcohol","booze","mojito"]},"beer-mug":{"a":"Beer Mug","b":"1F37A","j":["bar","beer","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-beer-mugs":{"a":"Clinking Beer Mugs","b":"1F37B","j":["bar","beer","clink","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-glasses":{"a":"Clinking Glasses","b":"1F942","j":["celebrate","clink","drink","glass","beverage","party","alcohol","cheers","wine","champagne","toast"]},"tumbler-glass":{"a":"Tumbler Glass","b":"1F943","j":["glass","liquor","shot","tumbler","whisky","drink","beverage","drunk","alcohol","booze","bourbon","scotch"]},"pouring-liquid":{"a":"⊛ Pouring Liquid","b":"1FAD7","j":["drink","empty","glass","spill","cup","water"]},"cup-with-straw":{"a":"Cup with Straw","b":"1F964","j":["juice","soda","malt","soft drink","water","drink"]},"bubble-tea":{"a":"Bubble Tea","b":"1F9CB","j":["bubble","milk","pearl","tea","taiwan","boba","milk tea","straw"]},"beverage-box":{"a":"Beverage Box","b":"1F9C3","j":["beverage","box","juice","straw","sweet","drink"]},"mate":{"a":"Mate","b":"1F9C9","j":["drink","tea","beverage"]},"ice":{"a":"Ice","b":"1F9CA","j":["cold","ice cube","iceberg","water"]},"chopsticks":{"a":"Chopsticks","b":"1F962","j":["hashi","jeotgarak","kuaizi","food"]},"fork-and-knife-with-plate":{"a":"Fork and Knife with Plate","b":"1F37D","j":["cooking","fork","knife","plate","food","eat","meal","lunch","dinner","restaurant"]},"fork-and-knife":{"a":"Fork and Knife","b":"1F374","j":["cooking","cutlery","fork","knife","kitchen"]},"spoon":{"a":"Spoon","b":"1F944","j":["tableware","cutlery","kitchen"]},"kitchen-knife":{"a":"Kitchen Knife","b":"1F52A","j":["cooking","hocho","knife","tool","weapon","blade","cutlery","kitchen"]},"jar":{"a":"⊛ Jar","b":"1FAD9","j":["condiment","container","empty","sauce","store"]},"amphora":{"a":"Amphora","b":"1F3FA","j":["Aquarius","cooking","drink","jug","zodiac","vase","jar"]},"globe-showing-europeafrica":{"a":"Globe Showing Europe-Africa","b":"1F30D","j":["Africa","earth","Europe","globe","globe showing Europe-Africa","world","globe_showing_europe_africa","international"]},"globe-showing-americas":{"a":"Globe Showing Americas","b":"1F30E","j":["Americas","earth","globe","globe showing Americas","world","USA","international"]},"globe-showing-asiaaustralia":{"a":"Globe Showing Asia-Australia","b":"1F30F","j":["Asia","Australia","earth","globe","globe showing Asia-Australia","world","globe_showing_asia_australia","east","international"]},"globe-with-meridians":{"a":"Globe with Meridians","b":"1F310","j":["earth","globe","meridians","world","international","internet","interweb","i18n"]},"world-map":{"a":"World Map","b":"1F5FA","j":["map","world","location","direction"]},"map-of-japan":{"a":"Map of Japan","b":"1F5FE","j":["Japan","map","map of Japan","nation","country","japanese","asia"]},"compass":{"a":"Compass","b":"1F9ED","j":["magnetic","navigation","orienteering"]},"snowcapped-mountain":{"a":"Snow-Capped Mountain","b":"1F3D4","j":["cold","mountain","snow","snow-capped mountain","snow_capped_mountain","photo","nature","environment","winter"]},"mountain":{"a":"Mountain","b":"26F0","j":["photo","nature","environment"]},"volcano":{"a":"Volcano","b":"1F30B","j":["eruption","mountain","photo","nature","disaster"]},"mount-fuji":{"a":"Mount Fuji","b":"1F5FB","j":["fuji","mountain","photo","nature","japanese"]},"camping":{"a":"Camping","b":"1F3D5","j":["photo","outdoors","tent"]},"beach-with-umbrella":{"a":"Beach with Umbrella","b":"1F3D6","j":["beach","umbrella","weather","summer","sunny","sand","mojito"]},"desert":{"a":"Desert","b":"1F3DC","j":["photo","warm","saharah"]},"desert-island":{"a":"Desert Island","b":"1F3DD","j":["desert","island","photo","tropical","mojito"]},"national-park":{"a":"National Park","b":"1F3DE","j":["park","photo","environment","nature"]},"stadium":{"a":"Stadium","b":"1F3DF","j":["photo","place","sports","concert","venue"]},"classical-building":{"a":"Classical Building","b":"1F3DB","j":["classical","art","culture","history"]},"building-construction":{"a":"Building Construction","b":"1F3D7","j":["construction","wip","working","progress"]},"brick":{"a":"Brick","b":"1F9F1","j":["bricks","clay","mortar","wall"]},"rock":{"a":"Rock","b":"1FAA8","j":["boulder","heavy","solid","stone"]},"wood":{"a":"Wood","b":"1FAB5","j":["log","lumber","timber","nature","trunk"]},"hut":{"a":"Hut","b":"1F6D6","j":["house","roundhouse","yurt","structure"]},"houses":{"a":"Houses","b":"1F3D8","j":["buildings","photo"]},"derelict-house":{"a":"Derelict House","b":"1F3DA","j":["derelict","house","abandon","evict","broken","building"]},"house":{"a":"House","b":"1F3E0","j":["home","building"]},"house-with-garden":{"a":"House with Garden","b":"1F3E1","j":["garden","home","house","plant","nature"]},"office-building":{"a":"Office Building","b":"1F3E2","j":["building","bureau","work"]},"japanese-post-office":{"a":"Japanese Post Office","b":"1F3E3","j":["Japanese","Japanese post office","post","building","envelope","communication"]},"post-office":{"a":"Post Office","b":"1F3E4","j":["European","post","building","email"]},"hospital":{"a":"Hospital","b":"1F3E5","j":["doctor","medicine","building","health","surgery"]},"bank":{"a":"Bank","b":"1F3E6","j":["building","money","sales","cash","business","enterprise"]},"hotel":{"a":"Hotel","b":"1F3E8","j":["building","accomodation","checkin"]},"love-hotel":{"a":"Love Hotel","b":"1F3E9","j":["hotel","love","like","affection","dating"]},"convenience-store":{"a":"Convenience Store","b":"1F3EA","j":["convenience","store","building","shopping","groceries"]},"school":{"a":"School","b":"1F3EB","j":["building","student","education","learn","teach"]},"department-store":{"a":"Department Store","b":"1F3EC","j":["department","store","building","shopping","mall"]},"factory":{"a":"Factory","b":"1F3ED","j":["building","industry","pollution","smoke"]},"japanese-castle":{"a":"Japanese Castle","b":"1F3EF","j":["castle","Japanese","photo","building"]},"castle":{"a":"Castle","b":"1F3F0","j":["European","building","royalty","history"]},"wedding":{"a":"Wedding","b":"1F492","j":["chapel","romance","love","like","affection","couple","marriage","bride","groom"]},"tokyo-tower":{"a":"Tokyo Tower","b":"1F5FC","j":["Tokyo","tower","photo","japanese"]},"statue-of-liberty":{"a":"Statue of Liberty","b":"1F5FD","j":["liberty","statue","american","newyork"]},"church":{"a":"Church","b":"26EA","j":["Christian","cross","religion","building","christ"]},"mosque":{"a":"Mosque","b":"1F54C","j":["islam","Muslim","religion","worship","minaret"]},"hindu-temple":{"a":"Hindu Temple","b":"1F6D5","j":["hindu","temple","religion"]},"synagogue":{"a":"Synagogue","b":"1F54D","j":["Jew","Jewish","religion","temple","judaism","worship","jewish"]},"shinto-shrine":{"a":"Shinto Shrine","b":"26E9","j":["religion","shinto","shrine","temple","japan","kyoto"]},"kaaba":{"a":"Kaaba","b":"1F54B","j":["islam","Muslim","religion","mecca","mosque"]},"fountain":{"a":"Fountain","b":"26F2","j":["photo","summer","water","fresh"]},"tent":{"a":"Tent","b":"26FA","j":["camping","photo","outdoors"]},"foggy":{"a":"Foggy","b":"1F301","j":["fog","photo","mountain"]},"night-with-stars":{"a":"Night with Stars","b":"1F303","j":["night","star","evening","city","downtown"]},"cityscape":{"a":"Cityscape","b":"1F3D9","j":["city","photo","night life","urban"]},"sunrise-over-mountains":{"a":"Sunrise over Mountains","b":"1F304","j":["morning","mountain","sun","sunrise","view","vacation","photo"]},"sunrise":{"a":"Sunrise","b":"1F305","j":["morning","sun","view","vacation","photo"]},"cityscape-at-dusk":{"a":"Cityscape at Dusk","b":"1F306","j":["city","dusk","evening","landscape","sunset","photo","sky","buildings"]},"sunset":{"a":"Sunset","b":"1F307","j":["dusk","sun","photo","good morning","dawn"]},"bridge-at-night":{"a":"Bridge at Night","b":"1F309","j":["bridge","night","photo","sanfrancisco"]},"hot-springs":{"a":"Hot Springs","b":"2668","j":["hot","hotsprings","springs","steaming","bath","warm","relax"]},"carousel-horse":{"a":"Carousel Horse","b":"1F3A0","j":["carousel","horse","photo","carnival"]},"playground-slide":{"a":"⊛ Playground Slide","b":"1F6DD","j":["amusement park","play","fun","park"]},"ferris-wheel":{"a":"Ferris Wheel","b":"1F3A1","j":["amusement park","ferris","wheel","photo","carnival","londoneye"]},"roller-coaster":{"a":"Roller Coaster","b":"1F3A2","j":["amusement park","coaster","roller","carnival","playground","photo","fun"]},"barber-pole":{"a":"Barber Pole","b":"1F488","j":["barber","haircut","pole","hair","salon","style"]},"circus-tent":{"a":"Circus Tent","b":"1F3AA","j":["circus","tent","festival","carnival","party"]},"locomotive":{"a":"Locomotive","b":"1F682","j":["engine","railway","steam","train","transportation","vehicle"]},"railway-car":{"a":"Railway Car","b":"1F683","j":["car","electric","railway","train","tram","trolleybus","transportation","vehicle"]},"highspeed-train":{"a":"High-Speed Train","b":"1F684","j":["high-speed train","railway","shinkansen","speed","train","high_speed_train","transportation","vehicle"]},"bullet-train":{"a":"Bullet Train","b":"1F685","j":["bullet","railway","shinkansen","speed","train","transportation","vehicle","fast","public","travel"]},"train":{"a":"Train","b":"1F686","j":["railway","transportation","vehicle"]},"metro":{"a":"Metro","b":"1F687","j":["subway","transportation","blue-square","mrt","underground","tube"]},"light-rail":{"a":"Light Rail","b":"1F688","j":["railway","transportation","vehicle"]},"station":{"a":"Station","b":"1F689","j":["railway","train","transportation","vehicle","public"]},"tram":{"a":"Tram","b":"1F68A","j":["trolleybus","transportation","vehicle"]},"monorail":{"a":"Monorail","b":"1F69D","j":["vehicle","transportation"]},"mountain-railway":{"a":"Mountain Railway","b":"1F69E","j":["car","mountain","railway","transportation","vehicle"]},"tram-car":{"a":"Tram Car","b":"1F68B","j":["car","tram","trolleybus","transportation","vehicle","carriage","public","travel"]},"bus":{"a":"Bus","b":"1F68C","j":["vehicle","car","transportation"]},"oncoming-bus":{"a":"Oncoming Bus","b":"1F68D","j":["bus","oncoming","vehicle","transportation"]},"trolleybus":{"a":"Trolleybus","b":"1F68E","j":["bus","tram","trolley","bart","transportation","vehicle"]},"minibus":{"a":"Minibus","b":"1F690","j":["bus","vehicle","car","transportation"]},"ambulance":{"a":"Ambulance","b":"1F691","j":["vehicle","health","911","hospital"]},"fire-engine":{"a":"Fire Engine","b":"1F692","j":["engine","fire","truck","transportation","cars","vehicle"]},"police-car":{"a":"Police Car","b":"1F693","j":["car","patrol","police","vehicle","cars","transportation","law","legal","enforcement"]},"oncoming-police-car":{"a":"Oncoming Police Car","b":"1F694","j":["car","oncoming","police","vehicle","law","legal","enforcement","911"]},"taxi":{"a":"Taxi","b":"1F695","j":["vehicle","uber","cars","transportation"]},"oncoming-taxi":{"a":"Oncoming Taxi","b":"1F696","j":["oncoming","taxi","vehicle","cars","uber"]},"automobile":{"a":"Automobile","b":"1F697","j":["car","red","transportation","vehicle"]},"oncoming-automobile":{"a":"Oncoming Automobile","b":"1F698","j":["automobile","car","oncoming","vehicle","transportation"]},"sport-utility-vehicle":{"a":"Sport Utility Vehicle","b":"1F699","j":["recreational","sport utility","transportation","vehicle"]},"pickup-truck":{"a":"Pickup Truck","b":"1F6FB","j":["pick-up","pickup","truck","car","transportation"]},"delivery-truck":{"a":"Delivery Truck","b":"1F69A","j":["delivery","truck","cars","transportation"]},"articulated-lorry":{"a":"Articulated Lorry","b":"1F69B","j":["lorry","semi","truck","vehicle","cars","transportation","express"]},"tractor":{"a":"Tractor","b":"1F69C","j":["vehicle","car","farming","agriculture"]},"racing-car":{"a":"Racing Car","b":"1F3CE","j":["car","racing","sports","race","fast","formula","f1"]},"motorcycle":{"a":"Motorcycle","b":"1F3CD","j":["racing","race","sports","fast"]},"motor-scooter":{"a":"Motor Scooter","b":"1F6F5","j":["motor","scooter","vehicle","vespa","sasha"]},"manual-wheelchair":{"a":"Manual Wheelchair","b":"1F9BD","j":["accessibility"]},"motorized-wheelchair":{"a":"Motorized Wheelchair","b":"1F9BC","j":["accessibility"]},"auto-rickshaw":{"a":"Auto Rickshaw","b":"1F6FA","j":["tuk tuk","move","transportation"]},"bicycle":{"a":"Bicycle","b":"1F6B2","j":["bike","sports","exercise","hipster"]},"kick-scooter":{"a":"Kick Scooter","b":"1F6F4","j":["kick","scooter","vehicle","razor"]},"skateboard":{"a":"Skateboard","b":"1F6F9","j":["board"]},"roller-skate":{"a":"Roller Skate","b":"1F6FC","j":["roller","skate","footwear","sports"]},"bus-stop":{"a":"Bus Stop","b":"1F68F","j":["bus","stop","transportation","wait"]},"motorway":{"a":"Motorway","b":"1F6E3","j":["highway","road","cupertino","interstate"]},"railway-track":{"a":"Railway Track","b":"1F6E4","j":["railway","train","transportation"]},"oil-drum":{"a":"Oil Drum","b":"1F6E2","j":["drum","oil","barrell"]},"fuel-pump":{"a":"Fuel Pump","b":"26FD","j":["diesel","fuel","fuelpump","gas","pump","station","gas station","petroleum"]},"wheel":{"a":"⊛ Wheel","b":"1F6DE","j":["circle","tire","turn","car","transport"]},"police-car-light":{"a":"Police Car Light","b":"1F6A8","j":["beacon","car","light","police","revolving","ambulance","911","emergency","alert","error","pinged","law","legal"]},"horizontal-traffic-light":{"a":"Horizontal Traffic Light","b":"1F6A5","j":["light","signal","traffic","transportation"]},"vertical-traffic-light":{"a":"Vertical Traffic Light","b":"1F6A6","j":["light","signal","traffic","transportation","driving"]},"stop-sign":{"a":"Stop Sign","b":"1F6D1","j":["octagonal","sign","stop"]},"construction":{"a":"Construction","b":"1F6A7","j":["barrier","wip","progress","caution","warning"]},"anchor":{"a":"Anchor","b":"2693","j":["ship","tool","ferry","sea","boat"]},"ring-buoy":{"a":"⊛ Ring Buoy","b":"1F6DF","j":["float","life preserver","life saver","rescue","safety"]},"sailboat":{"a":"Sailboat","b":"26F5","j":["boat","resort","sea","yacht","ship","summer","transportation","water","sailing"]},"canoe":{"a":"Canoe","b":"1F6F6","j":["boat","paddle","water","ship"]},"speedboat":{"a":"Speedboat","b":"1F6A4","j":["boat","ship","transportation","vehicle","summer"]},"passenger-ship":{"a":"Passenger Ship","b":"1F6F3","j":["passenger","ship","yacht","cruise","ferry"]},"ferry":{"a":"Ferry","b":"26F4","j":["boat","passenger","ship","yacht"]},"motor-boat":{"a":"Motor Boat","b":"1F6E5","j":["boat","motorboat","ship"]},"ship":{"a":"Ship","b":"1F6A2","j":["boat","passenger","transportation","titanic","deploy"]},"airplane":{"a":"Airplane","b":"2708","j":["aeroplane","vehicle","transportation","flight","fly"]},"small-airplane":{"a":"Small Airplane","b":"1F6E9","j":["aeroplane","airplane","flight","transportation","fly","vehicle"]},"airplane-departure":{"a":"Airplane Departure","b":"1F6EB","j":["aeroplane","airplane","check-in","departure","departures","airport","flight","landing"]},"airplane-arrival":{"a":"Airplane Arrival","b":"1F6EC","j":["aeroplane","airplane","arrivals","arriving","landing","airport","flight","boarding"]},"parachute":{"a":"Parachute","b":"1FA82","j":["hang-glide","parasail","skydive","fly","glide"]},"seat":{"a":"Seat","b":"1F4BA","j":["chair","sit","airplane","transport","bus","flight","fly"]},"helicopter":{"a":"Helicopter","b":"1F681","j":["vehicle","transportation","fly"]},"suspension-railway":{"a":"Suspension Railway","b":"1F69F","j":["railway","suspension","vehicle","transportation"]},"mountain-cableway":{"a":"Mountain Cableway","b":"1F6A0","j":["cable","gondola","mountain","transportation","vehicle","ski"]},"aerial-tramway":{"a":"Aerial Tramway","b":"1F6A1","j":["aerial","cable","car","gondola","tramway","transportation","vehicle","ski"]},"satellite":{"a":"Satellite","b":"1F6F0","j":["space","communication","gps","orbit","spaceflight","NASA","ISS"]},"rocket":{"a":"Rocket","b":"1F680","j":["space","launch","ship","staffmode","NASA","outer space","outer_space","fly"]},"flying-saucer":{"a":"Flying Saucer","b":"1F6F8","j":["UFO","transportation","vehicle","ufo"]},"bellhop-bell":{"a":"Bellhop Bell","b":"1F6CE","j":["bell","bellhop","hotel","service"]},"luggage":{"a":"Luggage","b":"1F9F3","j":["packing","travel"]},"hourglass-done":{"a":"Hourglass Done","b":"231B","j":["sand","timer","time","clock","oldschool","limit","exam","quiz","test"]},"hourglass-not-done":{"a":"Hourglass Not Done","b":"23F3","j":["hourglass","sand","timer","oldschool","time","countdown"]},"watch":{"a":"Watch","b":"231A","j":["clock","time","accessories"]},"alarm-clock":{"a":"Alarm Clock","b":"23F0","j":["alarm","clock","time","wake"]},"stopwatch":{"a":"Stopwatch","b":"23F1","j":["clock","time","deadline"]},"timer-clock":{"a":"Timer Clock","b":"23F2","j":["clock","timer","alarm"]},"mantelpiece-clock":{"a":"Mantelpiece Clock","b":"1F570","j":["clock","time"]},"twelve-oclock":{"a":"Twelve O’Clock","b":"1F55B","j":["00","12","12:00","clock","o’clock","twelve","twelve_o_clock","time","noon","midnight","midday","late","early","schedule"]},"twelvethirty":{"a":"Twelve-Thirty","b":"1F567","j":["12","12:30","clock","thirty","twelve","twelve-thirty","twelve_thirty","time","late","early","schedule"]},"one-oclock":{"a":"One O’Clock","b":"1F550","j":["00","1","1:00","clock","o’clock","one","one_o_clock","time","late","early","schedule"]},"onethirty":{"a":"One-Thirty","b":"1F55C","j":["1","1:30","clock","one","one-thirty","thirty","one_thirty","time","late","early","schedule"]},"two-oclock":{"a":"Two O’Clock","b":"1F551","j":["00","2","2:00","clock","o’clock","two","two_o_clock","time","late","early","schedule"]},"twothirty":{"a":"Two-Thirty","b":"1F55D","j":["2","2:30","clock","thirty","two","two-thirty","two_thirty","time","late","early","schedule"]},"three-oclock":{"a":"Three O’Clock","b":"1F552","j":["00","3","3:00","clock","o’clock","three","three_o_clock","time","late","early","schedule"]},"threethirty":{"a":"Three-Thirty","b":"1F55E","j":["3","3:30","clock","thirty","three","three-thirty","three_thirty","time","late","early","schedule"]},"four-oclock":{"a":"Four O’Clock","b":"1F553","j":["00","4","4:00","clock","four","o’clock","four_o_clock","time","late","early","schedule"]},"fourthirty":{"a":"Four-Thirty","b":"1F55F","j":["4","4:30","clock","four","four-thirty","thirty","four_thirty","time","late","early","schedule"]},"five-oclock":{"a":"Five O’Clock","b":"1F554","j":["00","5","5:00","clock","five","o’clock","five_o_clock","time","late","early","schedule"]},"fivethirty":{"a":"Five-Thirty","b":"1F560","j":["5","5:30","clock","five","five-thirty","thirty","five_thirty","time","late","early","schedule"]},"six-oclock":{"a":"Six O’Clock","b":"1F555","j":["00","6","6:00","clock","o’clock","six","six_o_clock","time","late","early","schedule","dawn","dusk"]},"sixthirty":{"a":"Six-Thirty","b":"1F561","j":["6","6:30","clock","six","six-thirty","thirty","six_thirty","time","late","early","schedule"]},"seven-oclock":{"a":"Seven O’Clock","b":"1F556","j":["00","7","7:00","clock","o’clock","seven","seven_o_clock","time","late","early","schedule"]},"seventhirty":{"a":"Seven-Thirty","b":"1F562","j":["7","7:30","clock","seven","seven-thirty","thirty","seven_thirty","time","late","early","schedule"]},"eight-oclock":{"a":"Eight O’Clock","b":"1F557","j":["00","8","8:00","clock","eight","o’clock","eight_o_clock","time","late","early","schedule"]},"eightthirty":{"a":"Eight-Thirty","b":"1F563","j":["8","8:30","clock","eight","eight-thirty","thirty","eight_thirty","time","late","early","schedule"]},"nine-oclock":{"a":"Nine O’Clock","b":"1F558","j":["00","9","9:00","clock","nine","o’clock","nine_o_clock","time","late","early","schedule"]},"ninethirty":{"a":"Nine-Thirty","b":"1F564","j":["9","9:30","clock","nine","nine-thirty","thirty","nine_thirty","time","late","early","schedule"]},"ten-oclock":{"a":"Ten O’Clock","b":"1F559","j":["00","10","10:00","clock","o’clock","ten","ten_o_clock","time","late","early","schedule"]},"tenthirty":{"a":"Ten-Thirty","b":"1F565","j":["10","10:30","clock","ten","ten-thirty","thirty","ten_thirty","time","late","early","schedule"]},"eleven-oclock":{"a":"Eleven O’Clock","b":"1F55A","j":["00","11","11:00","clock","eleven","o’clock","eleven_o_clock","time","late","early","schedule"]},"eleventhirty":{"a":"Eleven-Thirty","b":"1F566","j":["11","11:30","clock","eleven","eleven-thirty","thirty","eleven_thirty","time","late","early","schedule"]},"new-moon":{"a":"New Moon","b":"1F311","j":["dark","moon","nature","twilight","planet","space","night","evening","sleep"]},"waxing-crescent-moon":{"a":"Waxing Crescent Moon","b":"1F312","j":["crescent","moon","waxing","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon":{"a":"First Quarter Moon","b":"1F313","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waxing-gibbous-moon":{"a":"Waxing Gibbous Moon","b":"1F314","j":["gibbous","moon","waxing","nature","night","sky","gray","twilight","planet","space","evening","sleep"]},"full-moon":{"a":"Full Moon","b":"1F315","j":["full","moon","nature","yellow","twilight","planet","space","night","evening","sleep"]},"waning-gibbous-moon":{"a":"Waning Gibbous Moon","b":"1F316","j":["gibbous","moon","waning","nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"]},"last-quarter-moon":{"a":"Last Quarter Moon","b":"1F317","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waning-crescent-moon":{"a":"Waning Crescent Moon","b":"1F318","j":["crescent","moon","waning","nature","twilight","planet","space","night","evening","sleep"]},"crescent-moon":{"a":"Crescent Moon","b":"1F319","j":["crescent","moon","night","sleep","sky","evening","magic"]},"new-moon-face":{"a":"New Moon Face","b":"1F31A","j":["face","moon","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon-face":{"a":"First Quarter Moon Face","b":"1F31B","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"last-quarter-moon-face":{"a":"Last Quarter Moon Face","b":"1F31C","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"thermometer":{"a":"Thermometer","b":"1F321","j":["weather","temperature","hot","cold"]},"sun":{"a":"Sun","b":"2600","j":["bright","rays","sunny","weather","nature","brightness","summer","beach","spring"]},"full-moon-face":{"a":"Full Moon Face","b":"1F31D","j":["bright","face","full","moon","nature","twilight","planet","space","night","evening","sleep"]},"sun-with-face":{"a":"Sun with Face","b":"1F31E","j":["bright","face","sun","nature","morning","sky"]},"ringed-planet":{"a":"Ringed Planet","b":"1FA90","j":["saturn","saturnine","outerspace"]},"star":{"a":"Star","b":"2B50","j":["night","yellow"]},"glowing-star":{"a":"Glowing Star","b":"1F31F","j":["glittery","glow","shining","sparkle","star","night","awesome","good","magic"]},"shooting-star":{"a":"Shooting Star","b":"1F320","j":["falling","shooting","star","night","photo"]},"milky-way":{"a":"Milky Way","b":"1F30C","j":["space","photo","stars"]},"cloud":{"a":"Cloud","b":"2601","j":["weather","sky"]},"sun-behind-cloud":{"a":"Sun Behind Cloud","b":"26C5","j":["cloud","sun","weather","nature","cloudy","morning","fall","spring"]},"cloud-with-lightning-and-rain":{"a":"Cloud with Lightning and Rain","b":"26C8","j":["cloud","rain","thunder","weather","lightning"]},"sun-behind-small-cloud":{"a":"Sun Behind Small Cloud","b":"1F324","j":["cloud","sun","weather"]},"sun-behind-large-cloud":{"a":"Sun Behind Large Cloud","b":"1F325","j":["cloud","sun","weather"]},"sun-behind-rain-cloud":{"a":"Sun Behind Rain Cloud","b":"1F326","j":["cloud","rain","sun","weather"]},"cloud-with-rain":{"a":"Cloud with Rain","b":"1F327","j":["cloud","rain","weather"]},"cloud-with-snow":{"a":"Cloud with Snow","b":"1F328","j":["cloud","cold","snow","weather"]},"cloud-with-lightning":{"a":"Cloud with Lightning","b":"1F329","j":["cloud","lightning","weather","thunder"]},"tornado":{"a":"Tornado","b":"1F32A","j":["cloud","whirlwind","weather","cyclone","twister"]},"fog":{"a":"Fog","b":"1F32B","j":["cloud","weather"]},"wind-face":{"a":"Wind Face","b":"1F32C","j":["blow","cloud","face","wind","gust","air"]},"cyclone":{"a":"Cyclone","b":"1F300","j":["dizzy","hurricane","twister","typhoon","weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado"]},"rainbow":{"a":"Rainbow","b":"1F308","j":["rain","nature","happy","unicorn_face","photo","sky","spring"]},"closed-umbrella":{"a":"Closed Umbrella","b":"1F302","j":["clothing","rain","umbrella","weather","drizzle"]},"umbrella":{"a":"Umbrella","b":"2602","j":["clothing","rain","weather","spring"]},"umbrella-with-rain-drops":{"a":"Umbrella with Rain Drops","b":"2614","j":["clothing","drop","rain","umbrella","rainy","weather","spring"]},"umbrella-on-ground":{"a":"Umbrella on Ground","b":"26F1","j":["rain","sun","umbrella","weather","summer"]},"high-voltage":{"a":"High Voltage","b":"26A1","j":["danger","electric","lightning","voltage","zap","thunder","weather","lightning bolt","fast"]},"snowflake":{"a":"Snowflake","b":"2744","j":["cold","snow","winter","season","weather","christmas","xmas"]},"snowman":{"a":"Snowman","b":"2603","j":["cold","snow","winter","season","weather","christmas","xmas","frozen"]},"snowman-without-snow":{"a":"Snowman Without Snow","b":"26C4","j":["cold","snow","snowman","winter","season","weather","christmas","xmas","frozen","without_snow"]},"comet":{"a":"Comet","b":"2604","j":["space"]},"fire":{"a":"Fire","b":"1F525","j":["flame","tool","hot","cook"]},"droplet":{"a":"Droplet","b":"1F4A7","j":["cold","comic","drop","sweat","water","drip","faucet","spring"]},"water-wave":{"a":"Water Wave","b":"1F30A","j":["ocean","water","wave","sea","nature","tsunami","disaster"]},"jackolantern":{"a":"Jack-O-Lantern","b":"1F383","j":["celebration","halloween","jack","jack-o-lantern","lantern","jack_o_lantern","light","pumpkin","creepy","fall"]},"christmas-tree":{"a":"Christmas Tree","b":"1F384","j":["celebration","Christmas","tree","festival","vacation","december","xmas"]},"fireworks":{"a":"Fireworks","b":"1F386","j":["celebration","photo","festival","carnival","congratulations"]},"sparkler":{"a":"Sparkler","b":"1F387","j":["celebration","fireworks","sparkle","stars","night","shine"]},"firecracker":{"a":"Firecracker","b":"1F9E8","j":["dynamite","explosive","fireworks","boom","explode","explosion"]},"sparkles":{"a":"Sparkles","b":"2728","j":["*","sparkle","star","stars","shine","shiny","cool","awesome","good","magic"]},"balloon":{"a":"Balloon","b":"1F388","j":["celebration","party","birthday","circus"]},"party-popper":{"a":"Party Popper","b":"1F389","j":["celebration","party","popper","tada","congratulations","birthday","magic","circus"]},"confetti-ball":{"a":"Confetti Ball","b":"1F38A","j":["ball","celebration","confetti","festival","party","birthday","circus"]},"tanabata-tree":{"a":"Tanabata Tree","b":"1F38B","j":["banner","celebration","Japanese","tree","plant","nature","branch","summer"]},"pine-decoration":{"a":"Pine Decoration","b":"1F38D","j":["bamboo","celebration","Japanese","pine","plant","nature","vegetable","panda"]},"japanese-dolls":{"a":"Japanese Dolls","b":"1F38E","j":["celebration","doll","festival","Japanese","Japanese dolls","japanese","toy","kimono"]},"carp-streamer":{"a":"Carp Streamer","b":"1F38F","j":["carp","celebration","streamer","fish","japanese","koinobori","banner"]},"wind-chime":{"a":"Wind Chime","b":"1F390","j":["bell","celebration","chime","wind","nature","ding","spring"]},"moon-viewing-ceremony":{"a":"Moon Viewing Ceremony","b":"1F391","j":["celebration","ceremony","moon","photo","japan","asia","tsukimi"]},"red-envelope":{"a":"Red Envelope","b":"1F9E7","j":["gift","good luck","hóngbāo","lai see","money"]},"ribbon":{"a":"Ribbon","b":"1F380","j":["celebration","decoration","pink","girl","bowtie"]},"wrapped-gift":{"a":"Wrapped Gift","b":"1F381","j":["box","celebration","gift","present","wrapped","birthday","christmas","xmas"]},"reminder-ribbon":{"a":"Reminder Ribbon","b":"1F397","j":["celebration","reminder","ribbon","sports","cause","support","awareness"]},"admission-tickets":{"a":"Admission Tickets","b":"1F39F","j":["admission","ticket","sports","concert","entrance"]},"ticket":{"a":"Ticket","b":"1F3AB","j":["admission","event","concert","pass"]},"military-medal":{"a":"Military Medal","b":"1F396","j":["celebration","medal","military","award","winning","army"]},"trophy":{"a":"Trophy","b":"1F3C6","j":["prize","win","award","contest","place","ftw","ceremony"]},"sports-medal":{"a":"Sports Medal","b":"1F3C5","j":["medal","award","winning"]},"1st-place-medal":{"a":"1st Place Medal","b":"1F947","j":["first","gold","medal","award","winning"]},"2nd-place-medal":{"a":"2nd Place Medal","b":"1F948","j":["medal","second","silver","award"]},"3rd-place-medal":{"a":"3rd Place Medal","b":"1F949","j":["bronze","medal","third","award"]},"soccer-ball":{"a":"Soccer Ball","b":"26BD","j":["ball","football","soccer","sports"]},"baseball":{"a":"Baseball","b":"26BE","j":["ball","sports","balls"]},"softball":{"a":"Softball","b":"1F94E","j":["ball","glove","underarm","sports","balls"]},"basketball":{"a":"Basketball","b":"1F3C0","j":["ball","hoop","sports","balls","NBA"]},"volleyball":{"a":"Volleyball","b":"1F3D0","j":["ball","game","sports","balls"]},"american-football":{"a":"American Football","b":"1F3C8","j":["american","ball","football","sports","balls","NFL"]},"rugby-football":{"a":"Rugby Football","b":"1F3C9","j":["ball","football","rugby","sports","team"]},"tennis":{"a":"Tennis","b":"1F3BE","j":["ball","racquet","sports","balls","green"]},"flying-disc":{"a":"Flying Disc","b":"1F94F","j":["ultimate","sports","frisbee"]},"bowling":{"a":"Bowling","b":"1F3B3","j":["ball","game","sports","fun","play"]},"cricket-game":{"a":"Cricket Game","b":"1F3CF","j":["ball","bat","game","sports"]},"field-hockey":{"a":"Field Hockey","b":"1F3D1","j":["ball","field","game","hockey","stick","sports"]},"ice-hockey":{"a":"Ice Hockey","b":"1F3D2","j":["game","hockey","ice","puck","stick","sports"]},"lacrosse":{"a":"Lacrosse","b":"1F94D","j":["ball","goal","stick","sports"]},"ping-pong":{"a":"Ping Pong","b":"1F3D3","j":["ball","bat","game","paddle","table tennis","sports","pingpong"]},"badminton":{"a":"Badminton","b":"1F3F8","j":["birdie","game","racquet","shuttlecock","sports"]},"boxing-glove":{"a":"Boxing Glove","b":"1F94A","j":["boxing","glove","sports","fighting"]},"martial-arts-uniform":{"a":"Martial Arts Uniform","b":"1F94B","j":["judo","karate","martial arts","taekwondo","uniform"]},"goal-net":{"a":"Goal Net","b":"1F945","j":["goal","net","sports"]},"flag-in-hole":{"a":"Flag in Hole","b":"26F3","j":["golf","hole","sports","business","flag","summer"]},"ice-skate":{"a":"Ice Skate","b":"26F8","j":["ice","skate","sports"]},"fishing-pole":{"a":"Fishing Pole","b":"1F3A3","j":["fish","pole","food","hobby","summer"]},"diving-mask":{"a":"Diving Mask","b":"1F93F","j":["diving","scuba","snorkeling","sport","ocean"]},"running-shirt":{"a":"Running Shirt","b":"1F3BD","j":["athletics","running","sash","shirt","play","pageant"]},"skis":{"a":"Skis","b":"1F3BF","j":["ski","snow","sports","winter","cold"]},"sled":{"a":"Sled","b":"1F6F7","j":["sledge","sleigh","luge","toboggan"]},"curling-stone":{"a":"Curling Stone","b":"1F94C","j":["game","rock","sports"]},"bullseye":{"a":"Bullseye","b":"1F3AF","j":["dart","direct hit","game","hit","target","direct_hit","play","bar"]},"yoyo":{"a":"Yo-Yo","b":"1FA80","j":["fluctuate","toy","yo-yo","yo_yo"]},"kite":{"a":"Kite","b":"1FA81","j":["fly","soar","wind"]},"pool-8-ball":{"a":"Pool 8 Ball","b":"1F3B1","j":["8","ball","billiard","eight","game","pool","hobby","luck","magic"]},"crystal-ball":{"a":"Crystal Ball","b":"1F52E","j":["ball","crystal","fairy tale","fantasy","fortune","tool","disco","party","magic","circus","fortune_teller"]},"magic-wand":{"a":"Magic Wand","b":"1FA84","j":["magic","witch","wizard","supernature","power"]},"nazar-amulet":{"a":"Nazar Amulet","b":"1F9FF","j":["bead","charm","evil-eye","nazar","talisman"]},"hamsa":{"a":"⊛ Hamsa","b":"1FAAC","j":["amulet","Fatima","hand","Mary","Miriam","protection","religion"]},"video-game":{"a":"Video Game","b":"1F3AE","j":["controller","game","play","console","PS4"]},"joystick":{"a":"Joystick","b":"1F579","j":["game","video game","play"]},"slot-machine":{"a":"Slot Machine","b":"1F3B0","j":["game","slot","bet","gamble","vegas","fruit machine","luck","casino"]},"game-die":{"a":"Game Die","b":"1F3B2","j":["dice","die","game","random","tabletop","play","luck"]},"puzzle-piece":{"a":"Puzzle Piece","b":"1F9E9","j":["clue","interlocking","jigsaw","piece","puzzle"]},"teddy-bear":{"a":"Teddy Bear","b":"1F9F8","j":["plaything","plush","stuffed","toy"]},"piata":{"a":"Piñata","b":"1FA85","j":["celebration","party","piñata","pinata","mexico","candy"]},"mirror-ball":{"a":"⊛ Mirror Ball","b":"1FAA9","j":["dance","disco","glitter","party"]},"nesting-dolls":{"a":"Nesting Dolls","b":"1FA86","j":["doll","nesting","russia","matryoshka","toy"]},"spade-suit":{"a":"Spade Suit","b":"2660","j":["card","game","poker","cards","suits","magic"]},"heart-suit":{"a":"Heart Suit","b":"2665","j":["card","game","poker","cards","magic","suits"]},"diamond-suit":{"a":"Diamond Suit","b":"2666","j":["card","game","poker","cards","magic","suits"]},"club-suit":{"a":"Club Suit","b":"2663","j":["card","game","poker","cards","magic","suits"]},"chess-pawn":{"a":"Chess Pawn","b":"265F","j":["chess","dupe","expendable"]},"joker":{"a":"Joker","b":"1F0CF","j":["card","game","wildcard","poker","cards","play","magic"]},"mahjong-red-dragon":{"a":"Mahjong Red Dragon","b":"1F004","j":["game","mahjong","red","play","chinese","kanji"]},"flower-playing-cards":{"a":"Flower Playing Cards","b":"1F3B4","j":["card","flower","game","Japanese","playing","sunset","red"]},"performing-arts":{"a":"Performing Arts","b":"1F3AD","j":["art","mask","performing","theater","theatre","acting","drama"]},"framed-picture":{"a":"Framed Picture","b":"1F5BC","j":["art","frame","museum","painting","picture","photography"]},"artist-palette":{"a":"Artist Palette","b":"1F3A8","j":["art","museum","painting","palette","design","paint","draw","colors"]},"thread":{"a":"Thread","b":"1F9F5","j":["needle","sewing","spool","string"]},"sewing-needle":{"a":"Sewing Needle","b":"1FAA1","j":["embroidery","needle","sewing","stitches","sutures","tailoring"]},"yarn":{"a":"Yarn","b":"1F9F6","j":["ball","crochet","knit"]},"knot":{"a":"Knot","b":"1FAA2","j":["rope","tangled","tie","twine","twist","scout"]},"glasses":{"a":"Glasses","b":"1F453","j":["clothing","eye","eyeglasses","eyewear","fashion","accessories","eyesight","nerdy","dork","geek"]},"sunglasses":{"a":"Sunglasses","b":"1F576","j":["dark","eye","eyewear","glasses","face","cool","accessories"]},"goggles":{"a":"Goggles","b":"1F97D","j":["eye protection","swimming","welding","eyes","protection","safety"]},"lab-coat":{"a":"Lab Coat","b":"1F97C","j":["doctor","experiment","scientist","chemist"]},"safety-vest":{"a":"Safety Vest","b":"1F9BA","j":["emergency","safety","vest","protection"]},"necktie":{"a":"Necktie","b":"1F454","j":["clothing","tie","shirt","suitup","formal","fashion","cloth","business"]},"tshirt":{"a":"T-Shirt","b":"1F455","j":["clothing","shirt","t-shirt","t_shirt","fashion","cloth","casual","tee"]},"jeans":{"a":"Jeans","b":"1F456","j":["clothing","pants","trousers","fashion","shopping"]},"scarf":{"a":"Scarf","b":"1F9E3","j":["neck","winter","clothes"]},"gloves":{"a":"Gloves","b":"1F9E4","j":["hand","hands","winter","clothes"]},"coat":{"a":"Coat","b":"1F9E5","j":["jacket"]},"socks":{"a":"Socks","b":"1F9E6","j":["stocking","stockings","clothes"]},"dress":{"a":"Dress","b":"1F457","j":["clothing","clothes","fashion","shopping"]},"kimono":{"a":"Kimono","b":"1F458","j":["clothing","dress","fashion","women","female","japanese"]},"sari":{"a":"Sari","b":"1F97B","j":["clothing","dress"]},"onepiece-swimsuit":{"a":"One-Piece Swimsuit","b":"1FA71","j":["bathing suit","one-piece swimsuit","one_piece_swimsuit","fashion"]},"briefs":{"a":"Briefs","b":"1FA72","j":["bathing suit","one-piece","swimsuit","underwear","clothing"]},"shorts":{"a":"Shorts","b":"1FA73","j":["bathing suit","pants","underwear","clothing"]},"bikini":{"a":"Bikini","b":"1F459","j":["clothing","swim","swimming","female","woman","girl","fashion","beach","summer"]},"womans-clothes":{"a":"Woman’S Clothes","b":"1F45A","j":["clothing","woman","woman’s clothes","woman_s_clothes","fashion","shopping_bags","female"]},"purse":{"a":"Purse","b":"1F45B","j":["clothing","coin","fashion","accessories","money","sales","shopping"]},"handbag":{"a":"Handbag","b":"1F45C","j":["bag","clothing","purse","fashion","accessory","accessories","shopping"]},"clutch-bag":{"a":"Clutch Bag","b":"1F45D","j":["bag","clothing","pouch","accessories","shopping"]},"shopping-bags":{"a":"Shopping Bags","b":"1F6CD","j":["bag","hotel","shopping","mall","buy","purchase"]},"backpack":{"a":"Backpack","b":"1F392","j":["bag","rucksack","satchel","school","student","education"]},"thong-sandal":{"a":"Thong Sandal","b":"1FA74","j":["beach sandals","sandals","thong sandals","thongs","zōri","footwear","summer"]},"mans-shoe":{"a":"Man’S Shoe","b":"1F45E","j":["clothing","man","man’s shoe","shoe","man_s_shoe","fashion","male"]},"running-shoe":{"a":"Running Shoe","b":"1F45F","j":["athletic","clothing","shoe","sneaker","shoes","sports","sneakers"]},"hiking-boot":{"a":"Hiking Boot","b":"1F97E","j":["backpacking","boot","camping","hiking"]},"flat-shoe":{"a":"Flat Shoe","b":"1F97F","j":["ballet flat","slip-on","slipper","ballet"]},"highheeled-shoe":{"a":"High-Heeled Shoe","b":"1F460","j":["clothing","heel","high-heeled shoe","shoe","woman","high_heeled_shoe","fashion","shoes","female","pumps","stiletto"]},"womans-sandal":{"a":"Woman’S Sandal","b":"1F461","j":["clothing","sandal","shoe","woman","woman’s sandal","woman_s_sandal","shoes","fashion","flip flops"]},"ballet-shoes":{"a":"Ballet Shoes","b":"1FA70","j":["ballet","dance"]},"womans-boot":{"a":"Woman’S Boot","b":"1F462","j":["boot","clothing","shoe","woman","woman’s boot","woman_s_boot","shoes","fashion"]},"crown":{"a":"Crown","b":"1F451","j":["clothing","king","queen","kod","leader","royalty","lord"]},"womans-hat":{"a":"Woman’S Hat","b":"1F452","j":["clothing","hat","woman","woman’s hat","woman_s_hat","fashion","accessories","female","lady","spring"]},"top-hat":{"a":"Top Hat","b":"1F3A9","j":["clothing","hat","top","tophat","magic","gentleman","classy","circus"]},"graduation-cap":{"a":"Graduation Cap","b":"1F393","j":["cap","celebration","clothing","graduation","hat","school","college","degree","university","legal","learn","education"]},"billed-cap":{"a":"Billed Cap","b":"1F9E2","j":["baseball cap","cap","baseball"]},"military-helmet":{"a":"Military Helmet","b":"1FA96","j":["army","helmet","military","soldier","warrior","protection"]},"rescue-workers-helmet":{"a":"Rescue Worker’S Helmet","b":"26D1","j":["aid","cross","face","hat","helmet","rescue worker’s helmet","rescue_worker_s_helmet","construction","build"]},"prayer-beads":{"a":"Prayer Beads","b":"1F4FF","j":["beads","clothing","necklace","prayer","religion","dhikr","religious"]},"lipstick":{"a":"Lipstick","b":"1F484","j":["cosmetics","makeup","female","girl","fashion","woman"]},"ring":{"a":"Ring","b":"1F48D","j":["diamond","wedding","propose","marriage","valentines","fashion","jewelry","gem","engagement"]},"gem-stone":{"a":"Gem Stone","b":"1F48E","j":["diamond","gem","jewel","blue","ruby","jewelry"]},"muted-speaker":{"a":"Muted Speaker","b":"1F507","j":["mute","quiet","silent","speaker","sound","volume","silence"]},"speaker-low-volume":{"a":"Speaker Low Volume","b":"1F508","j":["soft","sound","volume","silence","broadcast"]},"speaker-medium-volume":{"a":"Speaker Medium Volume","b":"1F509","j":["medium","volume","speaker","broadcast"]},"speaker-high-volume":{"a":"Speaker High Volume","b":"1F50A","j":["loud","volume","noise","noisy","speaker","broadcast"]},"loudspeaker":{"a":"Loudspeaker","b":"1F4E2","j":["loud","public address","volume","sound"]},"megaphone":{"a":"Megaphone","b":"1F4E3","j":["cheering","sound","speaker","volume"]},"postal-horn":{"a":"Postal Horn","b":"1F4EF","j":["horn","post","postal","instrument","music"]},"bell":{"a":"Bell","b":"1F514","j":["sound","notification","christmas","xmas","chime"]},"bell-with-slash":{"a":"Bell with Slash","b":"1F515","j":["bell","forbidden","mute","quiet","silent","sound","volume"]},"musical-score":{"a":"Musical Score","b":"1F3BC","j":["music","score","treble","clef","compose"]},"musical-note":{"a":"Musical Note","b":"1F3B5","j":["music","note","score","tone","sound"]},"musical-notes":{"a":"Musical Notes","b":"1F3B6","j":["music","note","notes","score"]},"studio-microphone":{"a":"Studio Microphone","b":"1F399","j":["mic","microphone","music","studio","sing","recording","artist","talkshow"]},"level-slider":{"a":"Level Slider","b":"1F39A","j":["level","music","slider","scale"]},"control-knobs":{"a":"Control Knobs","b":"1F39B","j":["control","knobs","music","dial"]},"microphone":{"a":"Microphone","b":"1F3A4","j":["karaoke","mic","sound","music","PA","sing","talkshow"]},"headphone":{"a":"Headphone","b":"1F3A7","j":["earbud","music","score","gadgets"]},"radio":{"a":"Radio","b":"1F4FB","j":["video","communication","music","podcast","program"]},"saxophone":{"a":"Saxophone","b":"1F3B7","j":["instrument","music","sax","jazz","blues"]},"accordion":{"a":"Accordion","b":"1FA97","j":["concertina","squeeze box","music"]},"guitar":{"a":"Guitar","b":"1F3B8","j":["instrument","music"]},"musical-keyboard":{"a":"Musical Keyboard","b":"1F3B9","j":["instrument","keyboard","music","piano","compose"]},"trumpet":{"a":"Trumpet","b":"1F3BA","j":["instrument","music","brass"]},"violin":{"a":"Violin","b":"1F3BB","j":["instrument","music","orchestra","symphony"]},"banjo":{"a":"Banjo","b":"1FA95","j":["music","stringed","instructment"]},"drum":{"a":"Drum","b":"1F941","j":["drumsticks","music","instrument","snare"]},"long-drum":{"a":"Long Drum","b":"1FA98","j":["beat","conga","drum","rhythm","music"]},"mobile-phone":{"a":"Mobile Phone","b":"1F4F1","j":["cell","mobile","phone","telephone","technology","apple","gadgets","dial"]},"mobile-phone-with-arrow":{"a":"Mobile Phone with Arrow","b":"1F4F2","j":["arrow","cell","mobile","phone","receive","iphone","incoming"]},"telephone":{"a":"Telephone","b":"260E","j":["phone","technology","communication","dial"]},"telephone-receiver":{"a":"Telephone Receiver","b":"1F4DE","j":["phone","receiver","telephone","technology","communication","dial"]},"pager":{"a":"Pager","b":"1F4DF","j":["bbcall","oldschool","90s"]},"fax-machine":{"a":"Fax Machine","b":"1F4E0","j":["fax","communication","technology"]},"battery":{"a":"Battery","b":"1F50B","j":["power","energy","sustain"]},"low-battery":{"a":"⊛ Low Battery","b":"1FAAB","j":["electronic","low energy","drained","dead"]},"electric-plug":{"a":"Electric Plug","b":"1F50C","j":["electric","electricity","plug","charger","power"]},"laptop":{"a":"Laptop","b":"1F4BB","j":["computer","pc","personal","technology","screen","display","monitor"]},"desktop-computer":{"a":"Desktop Computer","b":"1F5A5","j":["computer","desktop","technology","computing","screen"]},"printer":{"a":"Printer","b":"1F5A8","j":["computer","paper","ink"]},"keyboard":{"a":"Keyboard","b":"2328","j":["computer","technology","type","input","text"]},"computer-mouse":{"a":"Computer Mouse","b":"1F5B1","j":["computer","click"]},"trackball":{"a":"Trackball","b":"1F5B2","j":["computer","technology","trackpad"]},"computer-disk":{"a":"Computer Disk","b":"1F4BD","j":["computer","disk","minidisk","optical","technology","record","data","90s"]},"floppy-disk":{"a":"Floppy Disk","b":"1F4BE","j":["computer","disk","floppy","oldschool","technology","save","90s","80s"]},"optical-disk":{"a":"Optical Disk","b":"1F4BF","j":["cd","computer","disk","optical","technology","dvd","disc","90s"]},"dvd":{"a":"Dvd","b":"1F4C0","j":["blu-ray","computer","disk","optical","cd","disc"]},"abacus":{"a":"Abacus","b":"1F9EE","j":["calculation"]},"movie-camera":{"a":"Movie Camera","b":"1F3A5","j":["camera","cinema","movie","film","record"]},"film-frames":{"a":"Film Frames","b":"1F39E","j":["cinema","film","frames","movie"]},"film-projector":{"a":"Film Projector","b":"1F4FD","j":["cinema","film","movie","projector","video","tape","record"]},"clapper-board":{"a":"Clapper Board","b":"1F3AC","j":["clapper","movie","film","record"]},"television":{"a":"Television","b":"1F4FA","j":["tv","video","technology","program","oldschool","show"]},"camera":{"a":"Camera","b":"1F4F7","j":["video","gadgets","photography"]},"camera-with-flash":{"a":"Camera with Flash","b":"1F4F8","j":["camera","flash","video","photography","gadgets"]},"video-camera":{"a":"Video Camera","b":"1F4F9","j":["camera","video","film","record"]},"videocassette":{"a":"Videocassette","b":"1F4FC","j":["tape","vhs","video","record","oldschool","90s","80s"]},"magnifying-glass-tilted-left":{"a":"Magnifying Glass Tilted Left","b":"1F50D","j":["glass","magnifying","search","tool","zoom","find","detective"]},"magnifying-glass-tilted-right":{"a":"Magnifying Glass Tilted Right","b":"1F50E","j":["glass","magnifying","search","tool","zoom","find","detective"]},"candle":{"a":"Candle","b":"1F56F","j":["light","fire","wax"]},"light-bulb":{"a":"Light Bulb","b":"1F4A1","j":["bulb","comic","electric","idea","light","electricity"]},"flashlight":{"a":"Flashlight","b":"1F526","j":["electric","light","tool","torch","dark","camping","sight","night"]},"red-paper-lantern":{"a":"Red Paper Lantern","b":"1F3EE","j":["bar","lantern","light","red","paper","halloween","spooky"]},"diya-lamp":{"a":"Diya Lamp","b":"1FA94","j":["diya","lamp","oil","lighting"]},"notebook-with-decorative-cover":{"a":"Notebook with Decorative Cover","b":"1F4D4","j":["book","cover","decorated","notebook","classroom","notes","record","paper","study"]},"closed-book":{"a":"Closed Book","b":"1F4D5","j":["book","closed","read","library","knowledge","textbook","learn"]},"open-book":{"a":"Open Book","b":"1F4D6","j":["book","open","read","library","knowledge","literature","learn","study"]},"green-book":{"a":"Green Book","b":"1F4D7","j":["book","green","read","library","knowledge","study"]},"blue-book":{"a":"Blue Book","b":"1F4D8","j":["blue","book","read","library","knowledge","learn","study"]},"orange-book":{"a":"Orange Book","b":"1F4D9","j":["book","orange","read","library","knowledge","textbook","study"]},"books":{"a":"Books","b":"1F4DA","j":["book","literature","library","study"]},"notebook":{"a":"Notebook","b":"1F4D3","j":["stationery","record","notes","paper","study"]},"ledger":{"a":"Ledger","b":"1F4D2","j":["notebook","notes","paper"]},"page-with-curl":{"a":"Page with Curl","b":"1F4C3","j":["curl","document","page","documents","office","paper"]},"scroll":{"a":"Scroll","b":"1F4DC","j":["paper","documents","ancient","history"]},"page-facing-up":{"a":"Page Facing Up","b":"1F4C4","j":["document","page","documents","office","paper","information"]},"newspaper":{"a":"Newspaper","b":"1F4F0","j":["news","paper","press","headline"]},"rolledup-newspaper":{"a":"Rolled-Up Newspaper","b":"1F5DE","j":["news","newspaper","paper","rolled","rolled-up newspaper","rolled_up_newspaper","press","headline"]},"bookmark-tabs":{"a":"Bookmark Tabs","b":"1F4D1","j":["bookmark","mark","marker","tabs","favorite","save","order","tidy"]},"bookmark":{"a":"Bookmark","b":"1F516","j":["mark","favorite","label","save"]},"label":{"a":"Label","b":"1F3F7","j":["sale","tag"]},"money-bag":{"a":"Money Bag","b":"1F4B0","j":["bag","dollar","money","moneybag","payment","coins","sale"]},"coin":{"a":"Coin","b":"1FA99","j":["gold","metal","money","silver","treasure","currency"]},"yen-banknote":{"a":"Yen Banknote","b":"1F4B4","j":["banknote","bill","currency","money","note","yen","sales","japanese","dollar"]},"dollar-banknote":{"a":"Dollar Banknote","b":"1F4B5","j":["banknote","bill","currency","dollar","money","note","sales"]},"euro-banknote":{"a":"Euro Banknote","b":"1F4B6","j":["banknote","bill","currency","euro","money","note","sales","dollar"]},"pound-banknote":{"a":"Pound Banknote","b":"1F4B7","j":["banknote","bill","currency","money","note","pound","british","sterling","sales","bills","uk","england"]},"money-with-wings":{"a":"Money with Wings","b":"1F4B8","j":["banknote","bill","fly","money","wings","dollar","bills","payment","sale"]},"credit-card":{"a":"Credit Card","b":"1F4B3","j":["card","credit","money","sales","dollar","bill","payment","shopping"]},"receipt":{"a":"Receipt","b":"1F9FE","j":["accounting","bookkeeping","evidence","proof","expenses"]},"chart-increasing-with-yen":{"a":"Chart Increasing with Yen","b":"1F4B9","j":["chart","graph","growth","money","yen","green-square","presentation","stats"]},"envelope":{"a":"Envelope","b":"2709","j":["email","letter","postal","inbox","communication"]},"email":{"a":"E-Mail","b":"1F4E7","j":["e-mail","letter","mail","e_mail","communication","inbox"]},"incoming-envelope":{"a":"Incoming Envelope","b":"1F4E8","j":["e-mail","email","envelope","incoming","letter","receive","inbox"]},"envelope-with-arrow":{"a":"Envelope with Arrow","b":"1F4E9","j":["arrow","e-mail","email","envelope","outgoing","communication"]},"outbox-tray":{"a":"Outbox Tray","b":"1F4E4","j":["box","letter","mail","outbox","sent","tray","inbox","email"]},"inbox-tray":{"a":"Inbox Tray","b":"1F4E5","j":["box","inbox","letter","mail","receive","tray","email","documents"]},"package":{"a":"Package","b":"1F4E6","j":["box","parcel","mail","gift","cardboard","moving"]},"closed-mailbox-with-raised-flag":{"a":"Closed Mailbox with Raised Flag","b":"1F4EB","j":["closed","mail","mailbox","postbox","email","inbox","communication"]},"closed-mailbox-with-lowered-flag":{"a":"Closed Mailbox with Lowered Flag","b":"1F4EA","j":["closed","lowered","mail","mailbox","postbox","email","communication","inbox"]},"open-mailbox-with-raised-flag":{"a":"Open Mailbox with Raised Flag","b":"1F4EC","j":["mail","mailbox","open","postbox","email","inbox","communication"]},"open-mailbox-with-lowered-flag":{"a":"Open Mailbox with Lowered Flag","b":"1F4ED","j":["lowered","mail","mailbox","open","postbox","email","inbox"]},"postbox":{"a":"Postbox","b":"1F4EE","j":["mail","mailbox","email","letter","envelope"]},"ballot-box-with-ballot":{"a":"Ballot Box with Ballot","b":"1F5F3","j":["ballot","box","election","vote"]},"pencil":{"a":"Pencil","b":"270F","j":["stationery","write","paper","writing","school","study"]},"black-nib":{"a":"Black Nib","b":"2712","j":["nib","pen","stationery","writing","write"]},"fountain-pen":{"a":"Fountain Pen","b":"1F58B","j":["fountain","pen","stationery","writing","write"]},"pen":{"a":"Pen","b":"1F58A","j":["ballpoint","stationery","writing","write"]},"paintbrush":{"a":"Paintbrush","b":"1F58C","j":["painting","drawing","creativity","art"]},"crayon":{"a":"Crayon","b":"1F58D","j":["drawing","creativity"]},"memo":{"a":"Memo","b":"1F4DD","j":["pencil","write","documents","stationery","paper","writing","legal","exam","quiz","test","study","compose"]},"briefcase":{"a":"Briefcase","b":"1F4BC","j":["business","documents","work","law","legal","job","career"]},"file-folder":{"a":"File Folder","b":"1F4C1","j":["file","folder","documents","business","office"]},"open-file-folder":{"a":"Open File Folder","b":"1F4C2","j":["file","folder","open","documents","load"]},"card-index-dividers":{"a":"Card Index Dividers","b":"1F5C2","j":["card","dividers","index","organizing","business","stationery"]},"calendar":{"a":"Calendar","b":"1F4C5","j":["date","schedule"]},"tearoff-calendar":{"a":"Tear-off Calendar","b":"1F4C6","j":["calendar","tear-off calendar","tear_off_calendar","schedule","date","planning"]},"spiral-notepad":{"a":"Spiral Notepad","b":"1F5D2","j":["note","pad","spiral","memo","stationery"]},"spiral-calendar":{"a":"Spiral Calendar","b":"1F5D3","j":["calendar","pad","spiral","date","schedule","planning"]},"card-index":{"a":"Card Index","b":"1F4C7","j":["card","index","rolodex","business","stationery"]},"chart-increasing":{"a":"Chart Increasing","b":"1F4C8","j":["chart","graph","growth","trend","upward","presentation","stats","recovery","business","economics","money","sales","good","success"]},"chart-decreasing":{"a":"Chart Decreasing","b":"1F4C9","j":["chart","down","graph","trend","presentation","stats","recession","business","economics","money","sales","bad","failure"]},"bar-chart":{"a":"Bar Chart","b":"1F4CA","j":["bar","chart","graph","presentation","stats"]},"clipboard":{"a":"Clipboard","b":"1F4CB","j":["stationery","documents"]},"pushpin":{"a":"Pushpin","b":"1F4CC","j":["pin","stationery","mark","here"]},"round-pushpin":{"a":"Round Pushpin","b":"1F4CD","j":["pin","pushpin","stationery","location","map","here"]},"paperclip":{"a":"Paperclip","b":"1F4CE","j":["documents","stationery"]},"linked-paperclips":{"a":"Linked Paperclips","b":"1F587","j":["link","paperclip","documents","stationery"]},"straight-ruler":{"a":"Straight Ruler","b":"1F4CF","j":["ruler","straight edge","stationery","calculate","length","math","school","drawing","architect","sketch"]},"triangular-ruler":{"a":"Triangular Ruler","b":"1F4D0","j":["ruler","set","triangle","stationery","math","architect","sketch"]},"scissors":{"a":"Scissors","b":"2702","j":["cutting","tool","stationery","cut"]},"card-file-box":{"a":"Card File Box","b":"1F5C3","j":["box","card","file","business","stationery"]},"file-cabinet":{"a":"File Cabinet","b":"1F5C4","j":["cabinet","file","filing","organizing"]},"wastebasket":{"a":"Wastebasket","b":"1F5D1","j":["bin","trash","rubbish","garbage","toss"]},"locked":{"a":"Locked","b":"1F512","j":["closed","security","password","padlock"]},"unlocked":{"a":"Unlocked","b":"1F513","j":["lock","open","unlock","privacy","security"]},"locked-with-pen":{"a":"Locked with Pen","b":"1F50F","j":["ink","lock","nib","pen","privacy","security","secret"]},"locked-with-key":{"a":"Locked with Key","b":"1F510","j":["closed","key","lock","secure","security","privacy"]},"key":{"a":"Key","b":"1F511","j":["lock","password","door"]},"old-key":{"a":"Old Key","b":"1F5DD","j":["clue","key","lock","old","door","password"]},"hammer":{"a":"Hammer","b":"1F528","j":["tool","tools","build","create"]},"axe":{"a":"Axe","b":"1FA93","j":["chop","hatchet","split","wood","tool","cut"]},"pick":{"a":"Pick","b":"26CF","j":["mining","tool","tools","dig"]},"hammer-and-pick":{"a":"Hammer and Pick","b":"2692","j":["hammer","pick","tool","tools","build","create"]},"hammer-and-wrench":{"a":"Hammer and Wrench","b":"1F6E0","j":["hammer","spanner","tool","wrench","tools","build","create"]},"dagger":{"a":"Dagger","b":"1F5E1","j":["knife","weapon"]},"crossed-swords":{"a":"Crossed Swords","b":"2694","j":["crossed","swords","weapon"]},"water-pistol":{"a":"Water Pistol","b":"1F52B","j":["gun","handgun","pistol","revolver","tool","water","weapon","violence"]},"boomerang":{"a":"Boomerang","b":"1FA83","j":["australia","rebound","repercussion","weapon"]},"bow-and-arrow":{"a":"Bow and Arrow","b":"1F3F9","j":["archer","arrow","bow","Sagittarius","zodiac","sports"]},"shield":{"a":"Shield","b":"1F6E1","j":["weapon","protection","security"]},"carpentry-saw":{"a":"Carpentry Saw","b":"1FA9A","j":["carpenter","lumber","saw","tool","cut","chop"]},"wrench":{"a":"Wrench","b":"1F527","j":["spanner","tool","tools","diy","ikea","fix","maintainer"]},"screwdriver":{"a":"Screwdriver","b":"1FA9B","j":["screw","tool","tools"]},"nut-and-bolt":{"a":"Nut and Bolt","b":"1F529","j":["bolt","nut","tool","handy","tools","fix"]},"gear":{"a":"Gear","b":"2699","j":["cog","cogwheel","tool"]},"clamp":{"a":"Clamp","b":"1F5DC","j":["compress","tool","vice"]},"balance-scale":{"a":"Balance Scale","b":"2696","j":["balance","justice","Libra","scale","zodiac","law","fairness","weight"]},"white-cane":{"a":"White Cane","b":"1F9AF","j":["accessibility","blind","probing_cane"]},"link":{"a":"Link","b":"1F517","j":["rings","url"]},"chains":{"a":"Chains","b":"26D3","j":["chain","lock","arrest"]},"hook":{"a":"Hook","b":"1FA9D","j":["catch","crook","curve","ensnare","selling point","tools"]},"toolbox":{"a":"Toolbox","b":"1F9F0","j":["chest","mechanic","tool","tools","diy","fix","maintainer"]},"magnet":{"a":"Magnet","b":"1F9F2","j":["attraction","horseshoe","magnetic"]},"ladder":{"a":"Ladder","b":"1FA9C","j":["climb","rung","step","tools"]},"alembic":{"a":"Alembic","b":"2697","j":["chemistry","tool","distilling","science","experiment"]},"test-tube":{"a":"Test Tube","b":"1F9EA","j":["chemist","chemistry","experiment","lab","science"]},"petri-dish":{"a":"Petri Dish","b":"1F9EB","j":["bacteria","biologist","biology","culture","lab"]},"dna":{"a":"Dna","b":"1F9EC","j":["biologist","evolution","gene","genetics","life"]},"microscope":{"a":"Microscope","b":"1F52C","j":["science","tool","laboratory","experiment","zoomin","study"]},"telescope":{"a":"Telescope","b":"1F52D","j":["science","tool","stars","space","zoom","astronomy"]},"satellite-antenna":{"a":"Satellite Antenna","b":"1F4E1","j":["antenna","dish","satellite","communication","future","radio","space"]},"syringe":{"a":"Syringe","b":"1F489","j":["medicine","needle","shot","sick","health","hospital","drugs","blood","doctor","nurse"]},"drop-of-blood":{"a":"Drop of Blood","b":"1FA78","j":["bleed","blood donation","injury","medicine","menstruation","period","hurt","harm","wound"]},"pill":{"a":"Pill","b":"1F48A","j":["doctor","medicine","sick","health","pharmacy","drug"]},"adhesive-bandage":{"a":"Adhesive Bandage","b":"1FA79","j":["bandage","heal"]},"crutch":{"a":"⊛ Crutch","b":"1FA7C","j":["cane","disability","hurt","mobility aid","stick","accessibility","assist"]},"stethoscope":{"a":"Stethoscope","b":"1FA7A","j":["doctor","heart","medicine","health"]},"xray":{"a":"⊛ X-Ray","b":"1FA7B","j":["bones","doctor","medical","skeleton","x-ray","medicine"]},"door":{"a":"Door","b":"1F6AA","j":["house","entry","exit"]},"elevator":{"a":"Elevator","b":"1F6D7","j":["accessibility","hoist","lift"]},"mirror":{"a":"Mirror","b":"1FA9E","j":["reflection","reflector","speculum"]},"window":{"a":"Window","b":"1FA9F","j":["frame","fresh air","opening","transparent","view","scenery"]},"bed":{"a":"Bed","b":"1F6CF","j":["hotel","sleep","rest"]},"couch-and-lamp":{"a":"Couch and Lamp","b":"1F6CB","j":["couch","hotel","lamp","read","chill"]},"chair":{"a":"Chair","b":"1FA91","j":["seat","sit","furniture"]},"toilet":{"a":"Toilet","b":"1F6BD","j":["restroom","wc","washroom","bathroom","potty"]},"plunger":{"a":"Plunger","b":"1FAA0","j":["force cup","plumber","suction","toilet"]},"shower":{"a":"Shower","b":"1F6BF","j":["water","clean","bathroom"]},"bathtub":{"a":"Bathtub","b":"1F6C1","j":["bath","clean","shower","bathroom"]},"mouse-trap":{"a":"Mouse Trap","b":"1FAA4","j":["bait","mousetrap","snare","trap","cheese"]},"razor":{"a":"Razor","b":"1FA92","j":["sharp","shave","cut"]},"lotion-bottle":{"a":"Lotion Bottle","b":"1F9F4","j":["lotion","moisturizer","shampoo","sunscreen"]},"safety-pin":{"a":"Safety Pin","b":"1F9F7","j":["diaper","punk rock"]},"broom":{"a":"Broom","b":"1F9F9","j":["cleaning","sweeping","witch"]},"basket":{"a":"Basket","b":"1F9FA","j":["farming","laundry","picnic"]},"roll-of-paper":{"a":"Roll of Paper","b":"1F9FB","j":["paper towels","toilet paper","roll"]},"bucket":{"a":"Bucket","b":"1FAA3","j":["cask","pail","vat","water","container"]},"soap":{"a":"Soap","b":"1F9FC","j":["bar","bathing","cleaning","lather","soapdish"]},"bubbles":{"a":"⊛ Bubbles","b":"1FAE7","j":["burp","clean","soap","underwater","fun","carbonation","sparkling"]},"toothbrush":{"a":"Toothbrush","b":"1FAA5","j":["bathroom","brush","clean","dental","hygiene","teeth"]},"sponge":{"a":"Sponge","b":"1F9FD","j":["absorbing","cleaning","porous"]},"fire-extinguisher":{"a":"Fire Extinguisher","b":"1F9EF","j":["extinguish","fire","quench"]},"shopping-cart":{"a":"Shopping Cart","b":"1F6D2","j":["cart","shopping","trolley"]},"cigarette":{"a":"Cigarette","b":"1F6AC","j":["smoking","kills","tobacco","joint","smoke"]},"coffin":{"a":"Coffin","b":"26B0","j":["death","vampire","dead","die","rip","graveyard","cemetery","casket","funeral","box"]},"headstone":{"a":"Headstone","b":"1FAA6","j":["cemetery","grave","graveyard","tombstone","death","rip"]},"funeral-urn":{"a":"Funeral Urn","b":"26B1","j":["ashes","death","funeral","urn","dead","die","rip"]},"moai":{"a":"Moai","b":"1F5FF","j":["face","moyai","statue","rock","easter island"]},"placard":{"a":"Placard","b":"1FAA7","j":["demonstration","picket","protest","sign","announcement"]},"identification-card":{"a":"⊛ Identification Card","b":"1FAAA","j":["credentials","ID","license","security","document"]},"atm-sign":{"a":"Atm Sign","b":"1F3E7","j":["atm","ATM sign","automated","bank","teller","money","sales","cash","blue-square","payment"]},"litter-in-bin-sign":{"a":"Litter in Bin Sign","b":"1F6AE","j":["litter","litter bin","blue-square","sign","human","info"]},"potable-water":{"a":"Potable Water","b":"1F6B0","j":["drinking","potable","water","blue-square","liquid","restroom","cleaning","faucet"]},"wheelchair-symbol":{"a":"Wheelchair Symbol","b":"267F","j":["access","blue-square","disabled","accessibility"]},"mens-room":{"a":"Men’S Room","b":"1F6B9","j":["lavatory","man","men’s room","restroom","wc","men_s_room","toilet","blue-square","gender","male"]},"womens-room":{"a":"Women’S Room","b":"1F6BA","j":["lavatory","restroom","wc","woman","women’s room","women_s_room","purple-square","female","toilet","loo","gender"]},"restroom":{"a":"Restroom","b":"1F6BB","j":["lavatory","WC","blue-square","toilet","refresh","wc","gender"]},"baby-symbol":{"a":"Baby Symbol","b":"1F6BC","j":["baby","changing","orange-square","child"]},"water-closet":{"a":"Water Closet","b":"1F6BE","j":["closet","lavatory","restroom","water","wc","toilet","blue-square"]},"passport-control":{"a":"Passport Control","b":"1F6C2","j":["control","passport","custom","blue-square"]},"customs":{"a":"Customs","b":"1F6C3","j":["passport","border","blue-square"]},"baggage-claim":{"a":"Baggage Claim","b":"1F6C4","j":["baggage","claim","blue-square","airport","transport"]},"left-luggage":{"a":"Left Luggage","b":"1F6C5","j":["baggage","locker","luggage","blue-square","travel"]},"warning":{"a":"Warning","b":"26A0","j":["exclamation","wip","alert","error","problem","issue"]},"children-crossing":{"a":"Children Crossing","b":"1F6B8","j":["child","crossing","pedestrian","traffic","school","warning","danger","sign","driving","yellow-diamond"]},"no-entry":{"a":"No Entry","b":"26D4","j":["entry","forbidden","no","not","prohibited","traffic","limit","security","privacy","bad","denied","stop","circle"]},"prohibited":{"a":"Prohibited","b":"1F6AB","j":["entry","forbidden","no","not","forbid","stop","limit","denied","disallow","circle"]},"no-bicycles":{"a":"No Bicycles","b":"1F6B3","j":["bicycle","bike","forbidden","no","prohibited","cyclist","circle"]},"no-smoking":{"a":"No Smoking","b":"1F6AD","j":["forbidden","no","not","prohibited","smoking","cigarette","blue-square","smell","smoke"]},"no-littering":{"a":"No Littering","b":"1F6AF","j":["forbidden","litter","no","not","prohibited","trash","bin","garbage","circle"]},"nonpotable-water":{"a":"Non-Potable Water","b":"1F6B1","j":["non-drinking","non-potable","water","non_potable_water","drink","faucet","tap","circle"]},"no-pedestrians":{"a":"No Pedestrians","b":"1F6B7","j":["forbidden","no","not","pedestrian","prohibited","rules","crossing","walking","circle"]},"no-mobile-phones":{"a":"No Mobile Phones","b":"1F4F5","j":["cell","forbidden","mobile","no","phone","iphone","mute","circle"]},"no-one-under-eighteen":{"a":"No One Under Eighteen","b":"1F51E","j":["18","age restriction","eighteen","prohibited","underage","drink","pub","night","minor","circle"]},"radioactive":{"a":"Radioactive","b":"2622","j":["sign","nuclear","danger"]},"biohazard":{"a":"Biohazard","b":"2623","j":["sign","danger"]},"up-arrow":{"a":"Up Arrow","b":"2B06","j":["arrow","cardinal","direction","north","blue-square","continue","top"]},"upright-arrow":{"a":"Up-Right Arrow","b":"2197","j":["arrow","direction","intercardinal","northeast","up-right arrow","up_right_arrow","blue-square","point","diagonal"]},"right-arrow":{"a":"Right Arrow","b":"27A1","j":["arrow","cardinal","direction","east","blue-square","next"]},"downright-arrow":{"a":"Down-Right Arrow","b":"2198","j":["arrow","direction","down-right arrow","intercardinal","southeast","down_right_arrow","blue-square","diagonal"]},"down-arrow":{"a":"Down Arrow","b":"2B07","j":["arrow","cardinal","direction","down","south","blue-square","bottom"]},"downleft-arrow":{"a":"Down-Left Arrow","b":"2199","j":["arrow","direction","down-left arrow","intercardinal","southwest","down_left_arrow","blue-square","diagonal"]},"left-arrow":{"a":"Left Arrow","b":"2B05","j":["arrow","cardinal","direction","west","blue-square","previous","back"]},"upleft-arrow":{"a":"Up-Left Arrow","b":"2196","j":["arrow","direction","intercardinal","northwest","up-left arrow","up_left_arrow","blue-square","point","diagonal"]},"updown-arrow":{"a":"Up-Down Arrow","b":"2195","j":["arrow","up-down arrow","up_down_arrow","blue-square","direction","way","vertical"]},"leftright-arrow":{"a":"Left-Right Arrow","b":"2194","j":["arrow","left-right arrow","left_right_arrow","shape","direction","horizontal","sideways"]},"right-arrow-curving-left":{"a":"Right Arrow Curving Left","b":"21A9","j":["arrow","back","return","blue-square","undo","enter"]},"left-arrow-curving-right":{"a":"Left Arrow Curving Right","b":"21AA","j":["arrow","blue-square","return","rotate","direction"]},"right-arrow-curving-up":{"a":"Right Arrow Curving Up","b":"2934","j":["arrow","blue-square","direction","top"]},"right-arrow-curving-down":{"a":"Right Arrow Curving Down","b":"2935","j":["arrow","down","blue-square","direction","bottom"]},"clockwise-vertical-arrows":{"a":"Clockwise Vertical Arrows","b":"1F503","j":["arrow","clockwise","reload","sync","cycle","round","repeat"]},"counterclockwise-arrows-button":{"a":"Counterclockwise Arrows Button","b":"1F504","j":["anticlockwise","arrow","counterclockwise","withershins","blue-square","sync","cycle"]},"back-arrow":{"a":"Back Arrow","b":"1F519","j":["arrow","back","BACK arrow","words","return"]},"end-arrow":{"a":"End Arrow","b":"1F51A","j":["arrow","end","END arrow","words"]},"on-arrow":{"a":"On! Arrow","b":"1F51B","j":["arrow","mark","on","ON! arrow","words"]},"soon-arrow":{"a":"Soon Arrow","b":"1F51C","j":["arrow","soon","SOON arrow","words"]},"top-arrow":{"a":"Top Arrow","b":"1F51D","j":["arrow","top","TOP arrow","up","words","blue-square"]},"place-of-worship":{"a":"Place of Worship","b":"1F6D0","j":["religion","worship","church","temple","prayer"]},"atom-symbol":{"a":"Atom Symbol","b":"269B","j":["atheist","atom","science","physics","chemistry"]},"om":{"a":"Om","b":"1F549","j":["Hindu","religion","hinduism","buddhism","sikhism","jainism"]},"star-of-david":{"a":"Star of David","b":"2721","j":["David","Jew","Jewish","religion","star","star of David","judaism"]},"wheel-of-dharma":{"a":"Wheel of Dharma","b":"2638","j":["Buddhist","dharma","religion","wheel","hinduism","buddhism","sikhism","jainism"]},"yin-yang":{"a":"Yin Yang","b":"262F","j":["religion","tao","taoist","yang","yin","balance"]},"latin-cross":{"a":"Latin Cross","b":"271D","j":["Christian","cross","religion","christianity"]},"orthodox-cross":{"a":"Orthodox Cross","b":"2626","j":["Christian","cross","religion","suppedaneum"]},"star-and-crescent":{"a":"Star and Crescent","b":"262A","j":["islam","Muslim","religion"]},"peace-symbol":{"a":"Peace Symbol","b":"262E","j":["peace","hippie"]},"menorah":{"a":"Menorah","b":"1F54E","j":["candelabrum","candlestick","religion","hanukkah","candles","jewish"]},"dotted-sixpointed-star":{"a":"Dotted Six-Pointed Star","b":"1F52F","j":["dotted six-pointed star","fortune","star","dotted_six_pointed_star","purple-square","religion","jewish","hexagram"]},"aries":{"a":"Aries","b":"2648","j":["ram","zodiac","sign","purple-square","astrology"]},"taurus":{"a":"Taurus","b":"2649","j":["bull","ox","zodiac","purple-square","sign","astrology"]},"gemini":{"a":"Gemini","b":"264A","j":["twins","zodiac","sign","purple-square","astrology"]},"cancer":{"a":"Cancer","b":"264B","j":["crab","zodiac","sign","purple-square","astrology"]},"leo":{"a":"Leo","b":"264C","j":["lion","zodiac","sign","purple-square","astrology"]},"virgo":{"a":"Virgo","b":"264D","j":["zodiac","sign","purple-square","astrology"]},"libra":{"a":"Libra","b":"264E","j":["balance","justice","scales","zodiac","sign","purple-square","astrology"]},"scorpio":{"a":"Scorpio","b":"264F","j":["scorpion","scorpius","zodiac","sign","purple-square","astrology"]},"sagittarius":{"a":"Sagittarius","b":"2650","j":["archer","zodiac","sign","purple-square","astrology"]},"capricorn":{"a":"Capricorn","b":"2651","j":["goat","zodiac","sign","purple-square","astrology"]},"aquarius":{"a":"Aquarius","b":"2652","j":["bearer","water","zodiac","sign","purple-square","astrology"]},"pisces":{"a":"Pisces","b":"2653","j":["fish","zodiac","purple-square","sign","astrology"]},"ophiuchus":{"a":"Ophiuchus","b":"26CE","j":["bearer","serpent","snake","zodiac","sign","purple-square","constellation","astrology"]},"shuffle-tracks-button":{"a":"Shuffle Tracks Button","b":"1F500","j":["arrow","crossed","blue-square","shuffle","music","random"]},"repeat-button":{"a":"Repeat Button","b":"1F501","j":["arrow","clockwise","repeat","loop","record"]},"repeat-single-button":{"a":"Repeat Single Button","b":"1F502","j":["arrow","clockwise","once","blue-square","loop"]},"play-button":{"a":"Play Button","b":"25B6","j":["arrow","play","right","triangle","blue-square","direction"]},"fastforward-button":{"a":"Fast-Forward Button","b":"23E9","j":["arrow","double","fast","fast-forward button","forward","fast_forward_button","blue-square","play","speed","continue"]},"next-track-button":{"a":"Next Track Button","b":"23ED","j":["arrow","next scene","next track","triangle","forward","next","blue-square"]},"play-or-pause-button":{"a":"Play or Pause Button","b":"23EF","j":["arrow","pause","play","right","triangle","blue-square"]},"reverse-button":{"a":"Reverse Button","b":"25C0","j":["arrow","left","reverse","triangle","blue-square","direction"]},"fast-reverse-button":{"a":"Fast Reverse Button","b":"23EA","j":["arrow","double","rewind","play","blue-square"]},"last-track-button":{"a":"Last Track Button","b":"23EE","j":["arrow","previous scene","previous track","triangle","backward"]},"upwards-button":{"a":"Upwards Button","b":"1F53C","j":["arrow","button","red","blue-square","triangle","direction","point","forward","top"]},"fast-up-button":{"a":"Fast Up Button","b":"23EB","j":["arrow","double","blue-square","direction","top"]},"downwards-button":{"a":"Downwards Button","b":"1F53D","j":["arrow","button","down","red","blue-square","direction","bottom"]},"fast-down-button":{"a":"Fast Down Button","b":"23EC","j":["arrow","double","down","blue-square","direction","bottom"]},"pause-button":{"a":"Pause Button","b":"23F8","j":["bar","double","pause","vertical","blue-square"]},"stop-button":{"a":"Stop Button","b":"23F9","j":["square","stop","blue-square"]},"record-button":{"a":"Record Button","b":"23FA","j":["circle","record","blue-square"]},"eject-button":{"a":"Eject Button","b":"23CF","j":["eject","blue-square"]},"cinema":{"a":"Cinema","b":"1F3A6","j":["camera","film","movie","blue-square","record","curtain","stage","theater"]},"dim-button":{"a":"Dim Button","b":"1F505","j":["brightness","dim","low","sun","afternoon","warm","summer"]},"bright-button":{"a":"Bright Button","b":"1F506","j":["bright","brightness","sun","light"]},"antenna-bars":{"a":"Antenna Bars","b":"1F4F6","j":["antenna","bar","cell","mobile","phone","blue-square","reception","internet","connection","wifi","bluetooth","bars"]},"vibration-mode":{"a":"Vibration Mode","b":"1F4F3","j":["cell","mobile","mode","phone","telephone","vibration","orange-square"]},"mobile-phone-off":{"a":"Mobile Phone off","b":"1F4F4","j":["cell","mobile","off","phone","telephone","mute","orange-square","silence","quiet"]},"female-sign":{"a":"Female Sign","b":"2640","j":["woman","women","lady","girl"]},"male-sign":{"a":"Male Sign","b":"2642","j":["man","boy","men"]},"transgender-symbol":{"a":"Transgender Symbol","b":"26A7","j":["transgender","lgbtq"]},"multiply":{"a":"Multiply","b":"2716","j":["×","cancel","multiplication","sign","x","multiplication_sign","math","calculation"]},"plus":{"a":"Plus","b":"2795","j":["+","math","sign","plus_sign","calculation","addition","more","increase"]},"minus":{"a":"Minus","b":"2796","j":["-","−","math","sign","minus_sign","calculation","subtract","less"]},"divide":{"a":"Divide","b":"2797","j":["÷","division","math","sign","division_sign","calculation"]},"heavy-equals-sign":{"a":"⊛ Heavy Equals Sign","b":"1F7F0","j":["equality","math"]},"infinity":{"a":"Infinity","b":"267E","j":["forever","unbounded","universal"]},"double-exclamation-mark":{"a":"Double Exclamation Mark","b":"203C","j":["!","!!","bangbang","exclamation","mark","surprise"]},"exclamation-question-mark":{"a":"Exclamation Question Mark","b":"2049","j":["!","!?","?","exclamation","interrobang","mark","punctuation","question","wat","surprise"]},"red-question-mark":{"a":"Red Question Mark","b":"2753","j":["?","mark","punctuation","question","question_mark","doubt","confused"]},"white-question-mark":{"a":"White Question Mark","b":"2754","j":["?","mark","outlined","punctuation","question","doubts","gray","huh","confused"]},"white-exclamation-mark":{"a":"White Exclamation Mark","b":"2755","j":["!","exclamation","mark","outlined","punctuation","surprise","gray","wow","warning"]},"red-exclamation-mark":{"a":"Red Exclamation Mark","b":"2757","j":["!","exclamation","mark","punctuation","exclamation_mark","heavy_exclamation_mark","danger","surprise","wow","warning"]},"wavy-dash":{"a":"Wavy Dash","b":"3030","j":["dash","punctuation","wavy","draw","line","moustache","mustache","squiggle","scribble"]},"currency-exchange":{"a":"Currency Exchange","b":"1F4B1","j":["bank","currency","exchange","money","sales","dollar","travel"]},"heavy-dollar-sign":{"a":"Heavy Dollar Sign","b":"1F4B2","j":["currency","dollar","money","sales","payment","buck"]},"medical-symbol":{"a":"Medical Symbol","b":"2695","j":["aesculapius","medicine","staff","health","hospital"]},"recycling-symbol":{"a":"Recycling Symbol","b":"267B","j":["recycle","arrow","environment","garbage","trash"]},"fleurdelis":{"a":"Fleur-De-Lis","b":"269C","j":["fleur-de-lis","fleur_de_lis","decorative","scout"]},"trident-emblem":{"a":"Trident Emblem","b":"1F531","j":["anchor","emblem","ship","tool","trident","weapon","spear"]},"name-badge":{"a":"Name Badge","b":"1F4DB","j":["badge","name","fire","forbid"]},"japanese-symbol-for-beginner":{"a":"Japanese Symbol for Beginner","b":"1F530","j":["beginner","chevron","Japanese","Japanese symbol for beginner","leaf","badge","shield"]},"hollow-red-circle":{"a":"Hollow Red Circle","b":"2B55","j":["circle","large","o","red","round"]},"check-mark-button":{"a":"Check Mark Button","b":"2705","j":["✓","button","check","mark","green-square","ok","agree","vote","election","answer","tick"]},"check-box-with-check":{"a":"Check Box with Check","b":"2611","j":["✓","box","check","ok","agree","confirm","black-square","vote","election","yes","tick"]},"check-mark":{"a":"Check Mark","b":"2714","j":["✓","check","mark","ok","nike","answer","yes","tick"]},"cross-mark":{"a":"Cross Mark","b":"274C","j":["×","cancel","cross","mark","multiplication","multiply","x","no","delete","remove","red"]},"cross-mark-button":{"a":"Cross Mark Button","b":"274E","j":["×","mark","square","x","green-square","no","deny"]},"curly-loop":{"a":"Curly Loop","b":"27B0","j":["curl","loop","scribble","draw","shape","squiggle"]},"double-curly-loop":{"a":"Double Curly Loop","b":"27BF","j":["curl","double","loop","tape","cassette"]},"part-alternation-mark":{"a":"Part Alternation Mark","b":"303D","j":["mark","part","graph","presentation","stats","business","economics","bad"]},"eightspoked-asterisk":{"a":"Eight-Spoked Asterisk","b":"2733","j":["*","asterisk","eight-spoked asterisk","eight_spoked_asterisk","star","sparkle","green-square"]},"eightpointed-star":{"a":"Eight-Pointed Star","b":"2734","j":["*","eight-pointed star","star","eight_pointed_star","orange-square","shape","polygon"]},"sparkle":{"a":"Sparkle","b":"2747","j":["*","stars","green-square","awesome","good","fireworks"]},"copyright":{"a":"Copyright","b":"00A9","j":["c","ip","license","circle","law","legal"]},"registered":{"a":"Registered","b":"00AE","j":["r","alphabet","circle"]},"trade-mark":{"a":"Trade Mark","b":"2122","j":["mark","tm","trademark","brand","law","legal"]},"keycap":{"a":"Keycap: *","b":"002A-FE0F-20E3","j":["keycap_","star"]},"keycap-0":{"a":"Keycap: 0","b":"0030-FE0F-20E3","j":["keycap","0","numbers","blue-square","null"]},"keycap-1":{"a":"Keycap: 1","b":"0031-FE0F-20E3","j":["keycap","blue-square","numbers","1"]},"keycap-2":{"a":"Keycap: 2","b":"0032-FE0F-20E3","j":["keycap","numbers","2","prime","blue-square"]},"keycap-3":{"a":"Keycap: 3","b":"0033-FE0F-20E3","j":["keycap","3","numbers","prime","blue-square"]},"keycap-4":{"a":"Keycap: 4","b":"0034-FE0F-20E3","j":["keycap","4","numbers","blue-square"]},"keycap-5":{"a":"Keycap: 5","b":"0035-FE0F-20E3","j":["keycap","5","numbers","blue-square","prime"]},"keycap-6":{"a":"Keycap: 6","b":"0036-FE0F-20E3","j":["keycap","6","numbers","blue-square"]},"keycap-7":{"a":"Keycap: 7","b":"0037-FE0F-20E3","j":["keycap","7","numbers","blue-square","prime"]},"keycap-8":{"a":"Keycap: 8","b":"0038-FE0F-20E3","j":["keycap","8","blue-square","numbers"]},"keycap-9":{"a":"Keycap: 9","b":"0039-FE0F-20E3","j":["keycap","blue-square","numbers","9"]},"keycap-10":{"a":"Keycap: 10","b":"1F51F","j":["keycap","numbers","10","blue-square"]},"input-latin-uppercase":{"a":"Input Latin Uppercase","b":"1F520","j":["ABCD","input","latin","letters","uppercase","alphabet","words","blue-square"]},"input-latin-lowercase":{"a":"Input Latin Lowercase","b":"1F521","j":["abcd","input","latin","letters","lowercase","blue-square","alphabet"]},"input-numbers":{"a":"Input Numbers","b":"1F522","j":["1234","input","numbers","blue-square"]},"input-symbols":{"a":"Input Symbols","b":"1F523","j":["〒♪&%","input","blue-square","music","note","ampersand","percent","glyphs","characters"]},"input-latin-letters":{"a":"Input Latin Letters","b":"1F524","j":["abc","alphabet","input","latin","letters","blue-square"]},"a-button-blood-type":{"a":"A Button (Blood Type)","b":"1F170","j":["a","A button (blood type)","blood type","a_button","red-square","alphabet","letter"]},"ab-button-blood-type":{"a":"Ab Button (Blood Type)","b":"1F18E","j":["ab","AB button (blood type)","blood type","ab_button","red-square","alphabet"]},"b-button-blood-type":{"a":"B Button (Blood Type)","b":"1F171","j":["b","B button (blood type)","blood type","b_button","red-square","alphabet","letter"]},"cl-button":{"a":"Cl Button","b":"1F191","j":["cl","CL button","alphabet","words","red-square"]},"cool-button":{"a":"Cool Button","b":"1F192","j":["cool","COOL button","words","blue-square"]},"free-button":{"a":"Free Button","b":"1F193","j":["free","FREE button","blue-square","words"]},"information":{"a":"Information","b":"2139","j":["i","blue-square","alphabet","letter"]},"id-button":{"a":"Id Button","b":"1F194","j":["id","ID button","identity","purple-square","words"]},"circled-m":{"a":"Circled M","b":"24C2","j":["circle","circled M","m","alphabet","blue-circle","letter"]},"new-button":{"a":"New Button","b":"1F195","j":["new","NEW button","blue-square","words","start"]},"ng-button":{"a":"Ng Button","b":"1F196","j":["ng","NG button","blue-square","words","shape","icon"]},"o-button-blood-type":{"a":"O Button (Blood Type)","b":"1F17E","j":["blood type","o","O button (blood type)","o_button","alphabet","red-square","letter"]},"ok-button":{"a":"Ok Button","b":"1F197","j":["OK","OK button","good","agree","yes","blue-square"]},"p-button":{"a":"P Button","b":"1F17F","j":["P button","parking","cars","blue-square","alphabet","letter"]},"sos-button":{"a":"Sos Button","b":"1F198","j":["help","sos","SOS button","red-square","words","emergency","911"]},"up-button":{"a":"Up! Button","b":"1F199","j":["mark","up","UP! button","blue-square","above","high"]},"vs-button":{"a":"Vs Button","b":"1F19A","j":["versus","vs","VS button","words","orange-square"]},"japanese-here-button":{"a":"Japanese “Here” Button","b":"1F201","j":["“here”","Japanese","Japanese “here” button","katakana","ココ","blue-square","here","japanese","destination"]},"japanese-service-charge-button":{"a":"Japanese “Service Charge” Button","b":"1F202","j":["“service charge”","Japanese","Japanese “service charge” button","katakana","サ","japanese","blue-square"]},"japanese-monthly-amount-button":{"a":"Japanese “Monthly Amount” Button","b":"1F237","j":["“monthly amount”","ideograph","Japanese","Japanese “monthly amount” button","月","chinese","month","moon","japanese","orange-square","kanji"]},"japanese-not-free-of-charge-button":{"a":"Japanese “Not Free of Charge” Button","b":"1F236","j":["“not free of charge”","ideograph","Japanese","Japanese “not free of charge” button","有","orange-square","chinese","have","kanji"]},"japanese-reserved-button":{"a":"Japanese “Reserved” Button","b":"1F22F","j":["“reserved”","ideograph","Japanese","Japanese “reserved” button","指","chinese","point","green-square","kanji"]},"japanese-bargain-button":{"a":"Japanese “Bargain” Button","b":"1F250","j":["“bargain”","ideograph","Japanese","Japanese “bargain” button","得","chinese","kanji","obtain","get","circle"]},"japanese-discount-button":{"a":"Japanese “Discount” Button","b":"1F239","j":["“discount”","ideograph","Japanese","Japanese “discount” button","割","cut","divide","chinese","kanji","pink-square"]},"japanese-free-of-charge-button":{"a":"Japanese “Free of Charge” Button","b":"1F21A","j":["“free of charge”","ideograph","Japanese","Japanese “free of charge” button","無","nothing","chinese","kanji","japanese","orange-square"]},"japanese-prohibited-button":{"a":"Japanese “Prohibited” Button","b":"1F232","j":["“prohibited”","ideograph","Japanese","Japanese “prohibited” button","禁","kanji","japanese","chinese","forbidden","limit","restricted","red-square"]},"japanese-acceptable-button":{"a":"Japanese “Acceptable” Button","b":"1F251","j":["“acceptable”","ideograph","Japanese","Japanese “acceptable” button","可","ok","good","chinese","kanji","agree","yes","orange-circle"]},"japanese-application-button":{"a":"Japanese “Application” Button","b":"1F238","j":["“application”","ideograph","Japanese","Japanese “application” button","申","chinese","japanese","kanji","orange-square"]},"japanese-passing-grade-button":{"a":"Japanese “Passing Grade” Button","b":"1F234","j":["“passing grade”","ideograph","Japanese","Japanese “passing grade” button","合","japanese","chinese","join","kanji","red-square"]},"japanese-vacancy-button":{"a":"Japanese “Vacancy” Button","b":"1F233","j":["“vacancy”","ideograph","Japanese","Japanese “vacancy” button","空","kanji","japanese","chinese","empty","sky","blue-square"]},"japanese-congratulations-button":{"a":"Japanese “Congratulations” Button","b":"3297","j":["“congratulations”","ideograph","Japanese","Japanese “congratulations” button","祝","chinese","kanji","japanese","red-circle"]},"japanese-secret-button":{"a":"Japanese “Secret” Button","b":"3299","j":["“secret”","ideograph","Japanese","Japanese “secret” button","秘","privacy","chinese","sshh","kanji","red-circle"]},"japanese-open-for-business-button":{"a":"Japanese “Open for Business” Button","b":"1F23A","j":["“open for business”","ideograph","Japanese","Japanese “open for business” button","営","japanese","opening hours","orange-square"]},"japanese-no-vacancy-button":{"a":"Japanese “No Vacancy” Button","b":"1F235","j":["“no vacancy”","ideograph","Japanese","Japanese “no vacancy” button","満","full","chinese","japanese","red-square","kanji"]},"red-circle":{"a":"Red Circle","b":"1F534","j":["circle","geometric","red","shape","error","danger"]},"orange-circle":{"a":"Orange Circle","b":"1F7E0","j":["circle","orange","round"]},"yellow-circle":{"a":"Yellow Circle","b":"1F7E1","j":["circle","yellow","round"]},"green-circle":{"a":"Green Circle","b":"1F7E2","j":["circle","green","round"]},"blue-circle":{"a":"Blue Circle","b":"1F535","j":["blue","circle","geometric","shape","icon","button"]},"purple-circle":{"a":"Purple Circle","b":"1F7E3","j":["circle","purple","round"]},"brown-circle":{"a":"Brown Circle","b":"1F7E4","j":["brown","circle","round"]},"black-circle":{"a":"Black Circle","b":"26AB","j":["circle","geometric","shape","button","round"]},"white-circle":{"a":"White Circle","b":"26AA","j":["circle","geometric","shape","round"]},"red-square":{"a":"Red Square","b":"1F7E5","j":["red","square"]},"orange-square":{"a":"Orange Square","b":"1F7E7","j":["orange","square"]},"yellow-square":{"a":"Yellow Square","b":"1F7E8","j":["square","yellow"]},"green-square":{"a":"Green Square","b":"1F7E9","j":["green","square"]},"blue-square":{"a":"Blue Square","b":"1F7E6","j":["blue","square"]},"purple-square":{"a":"Purple Square","b":"1F7EA","j":["purple","square"]},"brown-square":{"a":"Brown Square","b":"1F7EB","j":["brown","square"]},"black-large-square":{"a":"Black Large Square","b":"2B1B","j":["geometric","square","shape","icon","button"]},"white-large-square":{"a":"White Large Square","b":"2B1C","j":["geometric","square","shape","icon","stone","button"]},"black-medium-square":{"a":"Black Medium Square","b":"25FC","j":["geometric","square","shape","button","icon"]},"white-medium-square":{"a":"White Medium Square","b":"25FB","j":["geometric","square","shape","stone","icon"]},"black-mediumsmall-square":{"a":"Black Medium-Small Square","b":"25FE","j":["black medium-small square","geometric","square","black_medium_small_square","icon","shape","button"]},"white-mediumsmall-square":{"a":"White Medium-Small Square","b":"25FD","j":["geometric","square","white medium-small square","white_medium_small_square","shape","stone","icon","button"]},"black-small-square":{"a":"Black Small Square","b":"25AA","j":["geometric","square","shape","icon"]},"white-small-square":{"a":"White Small Square","b":"25AB","j":["geometric","square","shape","icon"]},"large-orange-diamond":{"a":"Large Orange Diamond","b":"1F536","j":["diamond","geometric","orange","shape","jewel","gem"]},"large-blue-diamond":{"a":"Large Blue Diamond","b":"1F537","j":["blue","diamond","geometric","shape","jewel","gem"]},"small-orange-diamond":{"a":"Small Orange Diamond","b":"1F538","j":["diamond","geometric","orange","shape","jewel","gem"]},"small-blue-diamond":{"a":"Small Blue Diamond","b":"1F539","j":["blue","diamond","geometric","shape","jewel","gem"]},"red-triangle-pointed-up":{"a":"Red Triangle Pointed Up","b":"1F53A","j":["geometric","red","shape","direction","up","top"]},"red-triangle-pointed-down":{"a":"Red Triangle Pointed Down","b":"1F53B","j":["down","geometric","red","shape","direction","bottom"]},"diamond-with-a-dot":{"a":"Diamond with a Dot","b":"1F4A0","j":["comic","diamond","geometric","inside","jewel","blue","gem","crystal","fancy"]},"radio-button":{"a":"Radio Button","b":"1F518","j":["button","geometric","radio","input","old","music","circle"]},"white-square-button":{"a":"White Square Button","b":"1F533","j":["button","geometric","outlined","square","shape","input"]},"black-square-button":{"a":"Black Square Button","b":"1F532","j":["button","geometric","square","shape","input","frame"]},"chequered-flag":{"a":"Chequered Flag","b":"1F3C1","j":["checkered","chequered","racing","contest","finishline","race","gokart"]},"triangular-flag":{"a":"Triangular Flag","b":"1F6A9","j":["post","mark","milestone","place"]},"crossed-flags":{"a":"Crossed Flags","b":"1F38C","j":["celebration","cross","crossed","Japanese","japanese","nation","country","border"]},"black-flag":{"a":"Black Flag","b":"1F3F4","j":["waving","pirate"]},"white-flag":{"a":"White Flag","b":"1F3F3","j":["waving","losing","loser","lost","surrender","give up","fail"]},"rainbow-flag":{"a":"Rainbow Flag","b":"1F3F3-FE0F-200D-1F308","j":["pride","rainbow","flag","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"]},"transgender-flag":{"a":"Transgender Flag","b":"1F3F3-FE0F-200D-26A7-FE0F","j":["flag","light blue","pink","transgender","white","lgbtq"]},"pirate-flag":{"a":"Pirate Flag","b":"1F3F4-200D-2620-FE0F","j":["Jolly Roger","pirate","plunder","treasure","skull","crossbones","flag","banner"]},"flag-ascension-island":{"a":"Flag: Ascension Island","b":"1F1E6-1F1E8","j":["flag"]},"flag-andorra":{"a":"Flag: Andorra","b":"1F1E6-1F1E9","j":["flag","ad","nation","country","banner","andorra"]},"flag-united-arab-emirates":{"a":"Flag: United Arab Emirates","b":"1F1E6-1F1EA","j":["flag","united","arab","emirates","nation","country","banner","united_arab_emirates"]},"flag-afghanistan":{"a":"Flag: Afghanistan","b":"1F1E6-1F1EB","j":["flag","af","nation","country","banner","afghanistan"]},"flag-antigua--barbuda":{"a":"Flag: Antigua & Barbuda","b":"1F1E6-1F1EC","j":["flag","flag_antigua_barbuda","antigua","barbuda","nation","country","banner","antigua_barbuda"]},"flag-anguilla":{"a":"Flag: Anguilla","b":"1F1E6-1F1EE","j":["flag","ai","nation","country","banner","anguilla"]},"flag-albania":{"a":"Flag: Albania","b":"1F1E6-1F1F1","j":["flag","al","nation","country","banner","albania"]},"flag-armenia":{"a":"Flag: Armenia","b":"1F1E6-1F1F2","j":["flag","am","nation","country","banner","armenia"]},"flag-angola":{"a":"Flag: Angola","b":"1F1E6-1F1F4","j":["flag","ao","nation","country","banner","angola"]},"flag-antarctica":{"a":"Flag: Antarctica","b":"1F1E6-1F1F6","j":["flag","aq","nation","country","banner","antarctica"]},"flag-argentina":{"a":"Flag: Argentina","b":"1F1E6-1F1F7","j":["flag","ar","nation","country","banner","argentina"]},"flag-american-samoa":{"a":"Flag: American Samoa","b":"1F1E6-1F1F8","j":["flag","american","ws","nation","country","banner","american_samoa"]},"flag-austria":{"a":"Flag: Austria","b":"1F1E6-1F1F9","j":["flag","at","nation","country","banner","austria"]},"flag-australia":{"a":"Flag: Australia","b":"1F1E6-1F1FA","j":["flag","au","nation","country","banner","australia"]},"flag-aruba":{"a":"Flag: Aruba","b":"1F1E6-1F1FC","j":["flag","aw","nation","country","banner","aruba"]},"flag-land-islands":{"a":"Flag: Åland Islands","b":"1F1E6-1F1FD","j":["flag","flag_aland_islands","Åland","islands","nation","country","banner","aland_islands"]},"flag-azerbaijan":{"a":"Flag: Azerbaijan","b":"1F1E6-1F1FF","j":["flag","az","nation","country","banner","azerbaijan"]},"flag-bosnia--herzegovina":{"a":"Flag: Bosnia & Herzegovina","b":"1F1E7-1F1E6","j":["flag","flag_bosnia_herzegovina","bosnia","herzegovina","nation","country","banner","bosnia_herzegovina"]},"flag-barbados":{"a":"Flag: Barbados","b":"1F1E7-1F1E7","j":["flag","bb","nation","country","banner","barbados"]},"flag-bangladesh":{"a":"Flag: Bangladesh","b":"1F1E7-1F1E9","j":["flag","bd","nation","country","banner","bangladesh"]},"flag-belgium":{"a":"Flag: Belgium","b":"1F1E7-1F1EA","j":["flag","be","nation","country","banner","belgium"]},"flag-burkina-faso":{"a":"Flag: Burkina Faso","b":"1F1E7-1F1EB","j":["flag","burkina","faso","nation","country","banner","burkina_faso"]},"flag-bulgaria":{"a":"Flag: Bulgaria","b":"1F1E7-1F1EC","j":["flag","bg","nation","country","banner","bulgaria"]},"flag-bahrain":{"a":"Flag: Bahrain","b":"1F1E7-1F1ED","j":["flag","bh","nation","country","banner","bahrain"]},"flag-burundi":{"a":"Flag: Burundi","b":"1F1E7-1F1EE","j":["flag","bi","nation","country","banner","burundi"]},"flag-benin":{"a":"Flag: Benin","b":"1F1E7-1F1EF","j":["flag","bj","nation","country","banner","benin"]},"flag-st-barthlemy":{"a":"Flag: St. Barthélemy","b":"1F1E7-1F1F1","j":["flag","flag_st_barthelemy","saint","barthélemy","nation","country","banner","st_barthelemy"]},"flag-bermuda":{"a":"Flag: Bermuda","b":"1F1E7-1F1F2","j":["flag","bm","nation","country","banner","bermuda"]},"flag-brunei":{"a":"Flag: Brunei","b":"1F1E7-1F1F3","j":["flag","bn","darussalam","nation","country","banner","brunei"]},"flag-bolivia":{"a":"Flag: Bolivia","b":"1F1E7-1F1F4","j":["flag","bo","nation","country","banner","bolivia"]},"flag-caribbean-netherlands":{"a":"Flag: Caribbean Netherlands","b":"1F1E7-1F1F6","j":["flag","bonaire","nation","country","banner","caribbean_netherlands"]},"flag-brazil":{"a":"Flag: Brazil","b":"1F1E7-1F1F7","j":["flag","br","nation","country","banner","brazil"]},"flag-bahamas":{"a":"Flag: Bahamas","b":"1F1E7-1F1F8","j":["flag","bs","nation","country","banner","bahamas"]},"flag-bhutan":{"a":"Flag: Bhutan","b":"1F1E7-1F1F9","j":["flag","bt","nation","country","banner","bhutan"]},"flag-bouvet-island":{"a":"Flag: Bouvet Island","b":"1F1E7-1F1FB","j":["flag","norway"]},"flag-botswana":{"a":"Flag: Botswana","b":"1F1E7-1F1FC","j":["flag","bw","nation","country","banner","botswana"]},"flag-belarus":{"a":"Flag: Belarus","b":"1F1E7-1F1FE","j":["flag","by","nation","country","banner","belarus"]},"flag-belize":{"a":"Flag: Belize","b":"1F1E7-1F1FF","j":["flag","bz","nation","country","banner","belize"]},"flag-canada":{"a":"Flag: Canada","b":"1F1E8-1F1E6","j":["flag","ca","nation","country","banner","canada"]},"flag-cocos-keeling-islands":{"a":"Flag: Cocos (Keeling) Islands","b":"1F1E8-1F1E8","j":["flag","flag_cocos_islands","cocos","keeling","islands","nation","country","banner","cocos_islands"]},"flag-congo--kinshasa":{"a":"Flag: Congo - Kinshasa","b":"1F1E8-1F1E9","j":["flag","flag_congo_kinshasa","congo","democratic","republic","nation","country","banner","congo_kinshasa"]},"flag-central-african-republic":{"a":"Flag: Central African Republic","b":"1F1E8-1F1EB","j":["flag","central","african","republic","nation","country","banner","central_african_republic"]},"flag-congo--brazzaville":{"a":"Flag: Congo - Brazzaville","b":"1F1E8-1F1EC","j":["flag","flag_congo_brazzaville","congo","nation","country","banner","congo_brazzaville"]},"flag-switzerland":{"a":"Flag: Switzerland","b":"1F1E8-1F1ED","j":["flag","ch","nation","country","banner","switzerland"]},"flag-cte-divoire":{"a":"Flag: Côte D’Ivoire","b":"1F1E8-1F1EE","j":["flag","flag_cote_d_ivoire","ivory","coast","nation","country","banner","cote_d_ivoire"]},"flag-cook-islands":{"a":"Flag: Cook Islands","b":"1F1E8-1F1F0","j":["flag","cook","islands","nation","country","banner","cook_islands"]},"flag-chile":{"a":"Flag: Chile","b":"1F1E8-1F1F1","j":["flag","nation","country","banner","chile"]},"flag-cameroon":{"a":"Flag: Cameroon","b":"1F1E8-1F1F2","j":["flag","cm","nation","country","banner","cameroon"]},"flag-china":{"a":"Flag: China","b":"1F1E8-1F1F3","j":["flag","china","chinese","prc","country","nation","banner"]},"flag-colombia":{"a":"Flag: Colombia","b":"1F1E8-1F1F4","j":["flag","co","nation","country","banner","colombia"]},"flag-clipperton-island":{"a":"Flag: Clipperton Island","b":"1F1E8-1F1F5","j":["flag"]},"flag-costa-rica":{"a":"Flag: Costa Rica","b":"1F1E8-1F1F7","j":["flag","costa","rica","nation","country","banner","costa_rica"]},"flag-cuba":{"a":"Flag: Cuba","b":"1F1E8-1F1FA","j":["flag","cu","nation","country","banner","cuba"]},"flag-cape-verde":{"a":"Flag: Cape Verde","b":"1F1E8-1F1FB","j":["flag","cabo","verde","nation","country","banner","cape_verde"]},"flag-curaao":{"a":"Flag: Curaçao","b":"1F1E8-1F1FC","j":["flag","flag_curacao","curaçao","nation","country","banner","curacao"]},"flag-christmas-island":{"a":"Flag: Christmas Island","b":"1F1E8-1F1FD","j":["flag","christmas","island","nation","country","banner","christmas_island"]},"flag-cyprus":{"a":"Flag: Cyprus","b":"1F1E8-1F1FE","j":["flag","cy","nation","country","banner","cyprus"]},"flag-czechia":{"a":"Flag: Czechia","b":"1F1E8-1F1FF","j":["flag","cz","nation","country","banner","czechia"]},"flag-germany":{"a":"Flag: Germany","b":"1F1E9-1F1EA","j":["flag","german","nation","country","banner","germany"]},"flag-diego-garcia":{"a":"Flag: Diego Garcia","b":"1F1E9-1F1EC","j":["flag"]},"flag-djibouti":{"a":"Flag: Djibouti","b":"1F1E9-1F1EF","j":["flag","dj","nation","country","banner","djibouti"]},"flag-denmark":{"a":"Flag: Denmark","b":"1F1E9-1F1F0","j":["flag","dk","nation","country","banner","denmark"]},"flag-dominica":{"a":"Flag: Dominica","b":"1F1E9-1F1F2","j":["flag","dm","nation","country","banner","dominica"]},"flag-dominican-republic":{"a":"Flag: Dominican Republic","b":"1F1E9-1F1F4","j":["flag","dominican","republic","nation","country","banner","dominican_republic"]},"flag-algeria":{"a":"Flag: Algeria","b":"1F1E9-1F1FF","j":["flag","dz","nation","country","banner","algeria"]},"flag-ceuta--melilla":{"a":"Flag: Ceuta & Melilla","b":"1F1EA-1F1E6","j":["flag","flag_ceuta_melilla"]},"flag-ecuador":{"a":"Flag: Ecuador","b":"1F1EA-1F1E8","j":["flag","ec","nation","country","banner","ecuador"]},"flag-estonia":{"a":"Flag: Estonia","b":"1F1EA-1F1EA","j":["flag","ee","nation","country","banner","estonia"]},"flag-egypt":{"a":"Flag: Egypt","b":"1F1EA-1F1EC","j":["flag","eg","nation","country","banner","egypt"]},"flag-western-sahara":{"a":"Flag: Western Sahara","b":"1F1EA-1F1ED","j":["flag","western","sahara","nation","country","banner","western_sahara"]},"flag-eritrea":{"a":"Flag: Eritrea","b":"1F1EA-1F1F7","j":["flag","er","nation","country","banner","eritrea"]},"flag-spain":{"a":"Flag: Spain","b":"1F1EA-1F1F8","j":["flag","spain","nation","country","banner"]},"flag-ethiopia":{"a":"Flag: Ethiopia","b":"1F1EA-1F1F9","j":["flag","et","nation","country","banner","ethiopia"]},"flag-european-union":{"a":"Flag: European Union","b":"1F1EA-1F1FA","j":["flag","european","union","banner"]},"flag-finland":{"a":"Flag: Finland","b":"1F1EB-1F1EE","j":["flag","fi","nation","country","banner","finland"]},"flag-fiji":{"a":"Flag: Fiji","b":"1F1EB-1F1EF","j":["flag","fj","nation","country","banner","fiji"]},"flag-falkland-islands":{"a":"Flag: Falkland Islands","b":"1F1EB-1F1F0","j":["flag","falkland","islands","malvinas","nation","country","banner","falkland_islands"]},"flag-micronesia":{"a":"Flag: Micronesia","b":"1F1EB-1F1F2","j":["flag","micronesia","federated","states","nation","country","banner"]},"flag-faroe-islands":{"a":"Flag: Faroe Islands","b":"1F1EB-1F1F4","j":["flag","faroe","islands","nation","country","banner","faroe_islands"]},"flag-france":{"a":"Flag: France","b":"1F1EB-1F1F7","j":["flag","banner","nation","france","french","country"]},"flag-gabon":{"a":"Flag: Gabon","b":"1F1EC-1F1E6","j":["flag","ga","nation","country","banner","gabon"]},"flag-united-kingdom":{"a":"Flag: United Kingdom","b":"1F1EC-1F1E7","j":["flag","united","kingdom","great","britain","northern","ireland","nation","country","banner","british","UK","english","england","union jack","united_kingdom"]},"flag-grenada":{"a":"Flag: Grenada","b":"1F1EC-1F1E9","j":["flag","gd","nation","country","banner","grenada"]},"flag-georgia":{"a":"Flag: Georgia","b":"1F1EC-1F1EA","j":["flag","ge","nation","country","banner","georgia"]},"flag-french-guiana":{"a":"Flag: French Guiana","b":"1F1EC-1F1EB","j":["flag","french","guiana","nation","country","banner","french_guiana"]},"flag-guernsey":{"a":"Flag: Guernsey","b":"1F1EC-1F1EC","j":["flag","gg","nation","country","banner","guernsey"]},"flag-ghana":{"a":"Flag: Ghana","b":"1F1EC-1F1ED","j":["flag","gh","nation","country","banner","ghana"]},"flag-gibraltar":{"a":"Flag: Gibraltar","b":"1F1EC-1F1EE","j":["flag","gi","nation","country","banner","gibraltar"]},"flag-greenland":{"a":"Flag: Greenland","b":"1F1EC-1F1F1","j":["flag","gl","nation","country","banner","greenland"]},"flag-gambia":{"a":"Flag: Gambia","b":"1F1EC-1F1F2","j":["flag","gm","nation","country","banner","gambia"]},"flag-guinea":{"a":"Flag: Guinea","b":"1F1EC-1F1F3","j":["flag","gn","nation","country","banner","guinea"]},"flag-guadeloupe":{"a":"Flag: Guadeloupe","b":"1F1EC-1F1F5","j":["flag","gp","nation","country","banner","guadeloupe"]},"flag-equatorial-guinea":{"a":"Flag: Equatorial Guinea","b":"1F1EC-1F1F6","j":["flag","equatorial","gn","nation","country","banner","equatorial_guinea"]},"flag-greece":{"a":"Flag: Greece","b":"1F1EC-1F1F7","j":["flag","gr","nation","country","banner","greece"]},"flag-south-georgia--south-sandwich-islands":{"a":"Flag: South Georgia & South Sandwich Islands","b":"1F1EC-1F1F8","j":["flag","flag_south_georgia_south_sandwich_islands","south","georgia","sandwich","islands","nation","country","banner","south_georgia_south_sandwich_islands"]},"flag-guatemala":{"a":"Flag: Guatemala","b":"1F1EC-1F1F9","j":["flag","gt","nation","country","banner","guatemala"]},"flag-guam":{"a":"Flag: Guam","b":"1F1EC-1F1FA","j":["flag","gu","nation","country","banner","guam"]},"flag-guineabissau":{"a":"Flag: Guinea-Bissau","b":"1F1EC-1F1FC","j":["flag","flag_guinea_bissau","gw","bissau","nation","country","banner","guinea_bissau"]},"flag-guyana":{"a":"Flag: Guyana","b":"1F1EC-1F1FE","j":["flag","gy","nation","country","banner","guyana"]},"flag-hong-kong-sar-china":{"a":"Flag: Hong Kong Sar China","b":"1F1ED-1F1F0","j":["flag","hong","kong","nation","country","banner","hong_kong_sar_china"]},"flag-heard--mcdonald-islands":{"a":"Flag: Heard & Mcdonald Islands","b":"1F1ED-1F1F2","j":["flag","flag_heard_mcdonald_islands"]},"flag-honduras":{"a":"Flag: Honduras","b":"1F1ED-1F1F3","j":["flag","hn","nation","country","banner","honduras"]},"flag-croatia":{"a":"Flag: Croatia","b":"1F1ED-1F1F7","j":["flag","hr","nation","country","banner","croatia"]},"flag-haiti":{"a":"Flag: Haiti","b":"1F1ED-1F1F9","j":["flag","ht","nation","country","banner","haiti"]},"flag-hungary":{"a":"Flag: Hungary","b":"1F1ED-1F1FA","j":["flag","hu","nation","country","banner","hungary"]},"flag-canary-islands":{"a":"Flag: Canary Islands","b":"1F1EE-1F1E8","j":["flag","canary","islands","nation","country","banner","canary_islands"]},"flag-indonesia":{"a":"Flag: Indonesia","b":"1F1EE-1F1E9","j":["flag","nation","country","banner","indonesia"]},"flag-ireland":{"a":"Flag: Ireland","b":"1F1EE-1F1EA","j":["flag","ie","nation","country","banner","ireland"]},"flag-israel":{"a":"Flag: Israel","b":"1F1EE-1F1F1","j":["flag","il","nation","country","banner","israel"]},"flag-isle-of-man":{"a":"Flag: Isle of Man","b":"1F1EE-1F1F2","j":["flag","isle","man","nation","country","banner","isle_of_man"]},"flag-india":{"a":"Flag: India","b":"1F1EE-1F1F3","j":["flag","in","nation","country","banner","india"]},"flag-british-indian-ocean-territory":{"a":"Flag: British Indian Ocean Territory","b":"1F1EE-1F1F4","j":["flag","british","indian","ocean","territory","nation","country","banner","british_indian_ocean_territory"]},"flag-iraq":{"a":"Flag: Iraq","b":"1F1EE-1F1F6","j":["flag","iq","nation","country","banner","iraq"]},"flag-iran":{"a":"Flag: Iran","b":"1F1EE-1F1F7","j":["flag","iran","islamic","republic","nation","country","banner"]},"flag-iceland":{"a":"Flag: Iceland","b":"1F1EE-1F1F8","j":["flag","is","nation","country","banner","iceland"]},"flag-italy":{"a":"Flag: Italy","b":"1F1EE-1F1F9","j":["flag","italy","nation","country","banner"]},"flag-jersey":{"a":"Flag: Jersey","b":"1F1EF-1F1EA","j":["flag","je","nation","country","banner","jersey"]},"flag-jamaica":{"a":"Flag: Jamaica","b":"1F1EF-1F1F2","j":["flag","jm","nation","country","banner","jamaica"]},"flag-jordan":{"a":"Flag: Jordan","b":"1F1EF-1F1F4","j":["flag","jo","nation","country","banner","jordan"]},"flag-japan":{"a":"Flag: Japan","b":"1F1EF-1F1F5","j":["flag","japanese","nation","country","banner","japan","jp","ja"]},"flag-kenya":{"a":"Flag: Kenya","b":"1F1F0-1F1EA","j":["flag","ke","nation","country","banner","kenya"]},"flag-kyrgyzstan":{"a":"Flag: Kyrgyzstan","b":"1F1F0-1F1EC","j":["flag","kg","nation","country","banner","kyrgyzstan"]},"flag-cambodia":{"a":"Flag: Cambodia","b":"1F1F0-1F1ED","j":["flag","kh","nation","country","banner","cambodia"]},"flag-kiribati":{"a":"Flag: Kiribati","b":"1F1F0-1F1EE","j":["flag","ki","nation","country","banner","kiribati"]},"flag-comoros":{"a":"Flag: Comoros","b":"1F1F0-1F1F2","j":["flag","km","nation","country","banner","comoros"]},"flag-st-kitts--nevis":{"a":"Flag: St. Kitts & Nevis","b":"1F1F0-1F1F3","j":["flag","flag_st_kitts_nevis","saint","kitts","nevis","nation","country","banner","st_kitts_nevis"]},"flag-north-korea":{"a":"Flag: North Korea","b":"1F1F0-1F1F5","j":["flag","north","korea","nation","country","banner","north_korea"]},"flag-south-korea":{"a":"Flag: South Korea","b":"1F1F0-1F1F7","j":["flag","south","korea","nation","country","banner","south_korea"]},"flag-kuwait":{"a":"Flag: Kuwait","b":"1F1F0-1F1FC","j":["flag","kw","nation","country","banner","kuwait"]},"flag-cayman-islands":{"a":"Flag: Cayman Islands","b":"1F1F0-1F1FE","j":["flag","cayman","islands","nation","country","banner","cayman_islands"]},"flag-kazakhstan":{"a":"Flag: Kazakhstan","b":"1F1F0-1F1FF","j":["flag","kz","nation","country","banner","kazakhstan"]},"flag-laos":{"a":"Flag: Laos","b":"1F1F1-1F1E6","j":["flag","lao","democratic","republic","nation","country","banner","laos"]},"flag-lebanon":{"a":"Flag: Lebanon","b":"1F1F1-1F1E7","j":["flag","lb","nation","country","banner","lebanon"]},"flag-st-lucia":{"a":"Flag: St. Lucia","b":"1F1F1-1F1E8","j":["flag","saint","lucia","nation","country","banner","st_lucia"]},"flag-liechtenstein":{"a":"Flag: Liechtenstein","b":"1F1F1-1F1EE","j":["flag","li","nation","country","banner","liechtenstein"]},"flag-sri-lanka":{"a":"Flag: Sri Lanka","b":"1F1F1-1F1F0","j":["flag","sri","lanka","nation","country","banner","sri_lanka"]},"flag-liberia":{"a":"Flag: Liberia","b":"1F1F1-1F1F7","j":["flag","lr","nation","country","banner","liberia"]},"flag-lesotho":{"a":"Flag: Lesotho","b":"1F1F1-1F1F8","j":["flag","ls","nation","country","banner","lesotho"]},"flag-lithuania":{"a":"Flag: Lithuania","b":"1F1F1-1F1F9","j":["flag","lt","nation","country","banner","lithuania"]},"flag-luxembourg":{"a":"Flag: Luxembourg","b":"1F1F1-1F1FA","j":["flag","lu","nation","country","banner","luxembourg"]},"flag-latvia":{"a":"Flag: Latvia","b":"1F1F1-1F1FB","j":["flag","lv","nation","country","banner","latvia"]},"flag-libya":{"a":"Flag: Libya","b":"1F1F1-1F1FE","j":["flag","ly","nation","country","banner","libya"]},"flag-morocco":{"a":"Flag: Morocco","b":"1F1F2-1F1E6","j":["flag","ma","nation","country","banner","morocco"]},"flag-monaco":{"a":"Flag: Monaco","b":"1F1F2-1F1E8","j":["flag","mc","nation","country","banner","monaco"]},"flag-moldova":{"a":"Flag: Moldova","b":"1F1F2-1F1E9","j":["flag","moldova","republic","nation","country","banner"]},"flag-montenegro":{"a":"Flag: Montenegro","b":"1F1F2-1F1EA","j":["flag","me","nation","country","banner","montenegro"]},"flag-st-martin":{"a":"Flag: St. Martin","b":"1F1F2-1F1EB","j":["flag"]},"flag-madagascar":{"a":"Flag: Madagascar","b":"1F1F2-1F1EC","j":["flag","mg","nation","country","banner","madagascar"]},"flag-marshall-islands":{"a":"Flag: Marshall Islands","b":"1F1F2-1F1ED","j":["flag","marshall","islands","nation","country","banner","marshall_islands"]},"flag-north-macedonia":{"a":"Flag: North Macedonia","b":"1F1F2-1F1F0","j":["flag","macedonia","nation","country","banner","north_macedonia"]},"flag-mali":{"a":"Flag: Mali","b":"1F1F2-1F1F1","j":["flag","ml","nation","country","banner","mali"]},"flag-myanmar-burma":{"a":"Flag: Myanmar (Burma)","b":"1F1F2-1F1F2","j":["flag","flag_myanmar","mm","nation","country","banner","myanmar"]},"flag-mongolia":{"a":"Flag: Mongolia","b":"1F1F2-1F1F3","j":["flag","mn","nation","country","banner","mongolia"]},"flag-macao-sar-china":{"a":"Flag: Macao Sar China","b":"1F1F2-1F1F4","j":["flag","macao","nation","country","banner","macao_sar_china"]},"flag-northern-mariana-islands":{"a":"Flag: Northern Mariana Islands","b":"1F1F2-1F1F5","j":["flag","northern","mariana","islands","nation","country","banner","northern_mariana_islands"]},"flag-martinique":{"a":"Flag: Martinique","b":"1F1F2-1F1F6","j":["flag","mq","nation","country","banner","martinique"]},"flag-mauritania":{"a":"Flag: Mauritania","b":"1F1F2-1F1F7","j":["flag","mr","nation","country","banner","mauritania"]},"flag-montserrat":{"a":"Flag: Montserrat","b":"1F1F2-1F1F8","j":["flag","ms","nation","country","banner","montserrat"]},"flag-malta":{"a":"Flag: Malta","b":"1F1F2-1F1F9","j":["flag","mt","nation","country","banner","malta"]},"flag-mauritius":{"a":"Flag: Mauritius","b":"1F1F2-1F1FA","j":["flag","mu","nation","country","banner","mauritius"]},"flag-maldives":{"a":"Flag: Maldives","b":"1F1F2-1F1FB","j":["flag","mv","nation","country","banner","maldives"]},"flag-malawi":{"a":"Flag: Malawi","b":"1F1F2-1F1FC","j":["flag","mw","nation","country","banner","malawi"]},"flag-mexico":{"a":"Flag: Mexico","b":"1F1F2-1F1FD","j":["flag","mx","nation","country","banner","mexico"]},"flag-malaysia":{"a":"Flag: Malaysia","b":"1F1F2-1F1FE","j":["flag","my","nation","country","banner","malaysia"]},"flag-mozambique":{"a":"Flag: Mozambique","b":"1F1F2-1F1FF","j":["flag","mz","nation","country","banner","mozambique"]},"flag-namibia":{"a":"Flag: Namibia","b":"1F1F3-1F1E6","j":["flag","na","nation","country","banner","namibia"]},"flag-new-caledonia":{"a":"Flag: New Caledonia","b":"1F1F3-1F1E8","j":["flag","new","caledonia","nation","country","banner","new_caledonia"]},"flag-niger":{"a":"Flag: Niger","b":"1F1F3-1F1EA","j":["flag","ne","nation","country","banner","niger"]},"flag-norfolk-island":{"a":"Flag: Norfolk Island","b":"1F1F3-1F1EB","j":["flag","norfolk","island","nation","country","banner","norfolk_island"]},"flag-nigeria":{"a":"Flag: Nigeria","b":"1F1F3-1F1EC","j":["flag","nation","country","banner","nigeria"]},"flag-nicaragua":{"a":"Flag: Nicaragua","b":"1F1F3-1F1EE","j":["flag","ni","nation","country","banner","nicaragua"]},"flag-netherlands":{"a":"Flag: Netherlands","b":"1F1F3-1F1F1","j":["flag","nl","nation","country","banner","netherlands"]},"flag-norway":{"a":"Flag: Norway","b":"1F1F3-1F1F4","j":["flag","no","nation","country","banner","norway"]},"flag-nepal":{"a":"Flag: Nepal","b":"1F1F3-1F1F5","j":["flag","np","nation","country","banner","nepal"]},"flag-nauru":{"a":"Flag: Nauru","b":"1F1F3-1F1F7","j":["flag","nr","nation","country","banner","nauru"]},"flag-niue":{"a":"Flag: Niue","b":"1F1F3-1F1FA","j":["flag","nu","nation","country","banner","niue"]},"flag-new-zealand":{"a":"Flag: New Zealand","b":"1F1F3-1F1FF","j":["flag","new","zealand","nation","country","banner","new_zealand"]},"flag-oman":{"a":"Flag: Oman","b":"1F1F4-1F1F2","j":["flag","om_symbol","nation","country","banner","oman"]},"flag-panama":{"a":"Flag: Panama","b":"1F1F5-1F1E6","j":["flag","pa","nation","country","banner","panama"]},"flag-peru":{"a":"Flag: Peru","b":"1F1F5-1F1EA","j":["flag","pe","nation","country","banner","peru"]},"flag-french-polynesia":{"a":"Flag: French Polynesia","b":"1F1F5-1F1EB","j":["flag","french","polynesia","nation","country","banner","french_polynesia"]},"flag-papua-new-guinea":{"a":"Flag: Papua New Guinea","b":"1F1F5-1F1EC","j":["flag","papua","new","guinea","nation","country","banner","papua_new_guinea"]},"flag-philippines":{"a":"Flag: Philippines","b":"1F1F5-1F1ED","j":["flag","ph","nation","country","banner","philippines"]},"flag-pakistan":{"a":"Flag: Pakistan","b":"1F1F5-1F1F0","j":["flag","pk","nation","country","banner","pakistan"]},"flag-poland":{"a":"Flag: Poland","b":"1F1F5-1F1F1","j":["flag","pl","nation","country","banner","poland"]},"flag-st-pierre--miquelon":{"a":"Flag: St. Pierre & Miquelon","b":"1F1F5-1F1F2","j":["flag","flag_st_pierre_miquelon","saint","pierre","miquelon","nation","country","banner","st_pierre_miquelon"]},"flag-pitcairn-islands":{"a":"Flag: Pitcairn Islands","b":"1F1F5-1F1F3","j":["flag","pitcairn","nation","country","banner","pitcairn_islands"]},"flag-puerto-rico":{"a":"Flag: Puerto Rico","b":"1F1F5-1F1F7","j":["flag","puerto","rico","nation","country","banner","puerto_rico"]},"flag-palestinian-territories":{"a":"Flag: Palestinian Territories","b":"1F1F5-1F1F8","j":["flag","palestine","palestinian","territories","nation","country","banner","palestinian_territories"]},"flag-portugal":{"a":"Flag: Portugal","b":"1F1F5-1F1F9","j":["flag","pt","nation","country","banner","portugal"]},"flag-palau":{"a":"Flag: Palau","b":"1F1F5-1F1FC","j":["flag","pw","nation","country","banner","palau"]},"flag-paraguay":{"a":"Flag: Paraguay","b":"1F1F5-1F1FE","j":["flag","py","nation","country","banner","paraguay"]},"flag-qatar":{"a":"Flag: Qatar","b":"1F1F6-1F1E6","j":["flag","qa","nation","country","banner","qatar"]},"flag-runion":{"a":"Flag: Réunion","b":"1F1F7-1F1EA","j":["flag","flag_reunion","réunion","nation","country","banner","reunion"]},"flag-romania":{"a":"Flag: Romania","b":"1F1F7-1F1F4","j":["flag","ro","nation","country","banner","romania"]},"flag-serbia":{"a":"Flag: Serbia","b":"1F1F7-1F1F8","j":["flag","rs","nation","country","banner","serbia"]},"flag-russia":{"a":"Flag: Russia","b":"1F1F7-1F1FA","j":["flag","russian","federation","nation","country","banner","russia"]},"flag-rwanda":{"a":"Flag: Rwanda","b":"1F1F7-1F1FC","j":["flag","rw","nation","country","banner","rwanda"]},"flag-saudi-arabia":{"a":"Flag: Saudi Arabia","b":"1F1F8-1F1E6","j":["flag","nation","country","banner","saudi_arabia"]},"flag-solomon-islands":{"a":"Flag: Solomon Islands","b":"1F1F8-1F1E7","j":["flag","solomon","islands","nation","country","banner","solomon_islands"]},"flag-seychelles":{"a":"Flag: Seychelles","b":"1F1F8-1F1E8","j":["flag","sc","nation","country","banner","seychelles"]},"flag-sudan":{"a":"Flag: Sudan","b":"1F1F8-1F1E9","j":["flag","sd","nation","country","banner","sudan"]},"flag-sweden":{"a":"Flag: Sweden","b":"1F1F8-1F1EA","j":["flag","se","nation","country","banner","sweden"]},"flag-singapore":{"a":"Flag: Singapore","b":"1F1F8-1F1EC","j":["flag","sg","nation","country","banner","singapore"]},"flag-st-helena":{"a":"Flag: St. Helena","b":"1F1F8-1F1ED","j":["flag","saint","helena","ascension","tristan","cunha","nation","country","banner","st_helena"]},"flag-slovenia":{"a":"Flag: Slovenia","b":"1F1F8-1F1EE","j":["flag","si","nation","country","banner","slovenia"]},"flag-svalbard--jan-mayen":{"a":"Flag: Svalbard & Jan Mayen","b":"1F1F8-1F1EF","j":["flag","flag_svalbard_jan_mayen"]},"flag-slovakia":{"a":"Flag: Slovakia","b":"1F1F8-1F1F0","j":["flag","sk","nation","country","banner","slovakia"]},"flag-sierra-leone":{"a":"Flag: Sierra Leone","b":"1F1F8-1F1F1","j":["flag","sierra","leone","nation","country","banner","sierra_leone"]},"flag-san-marino":{"a":"Flag: San Marino","b":"1F1F8-1F1F2","j":["flag","san","marino","nation","country","banner","san_marino"]},"flag-senegal":{"a":"Flag: Senegal","b":"1F1F8-1F1F3","j":["flag","sn","nation","country","banner","senegal"]},"flag-somalia":{"a":"Flag: Somalia","b":"1F1F8-1F1F4","j":["flag","so","nation","country","banner","somalia"]},"flag-suriname":{"a":"Flag: Suriname","b":"1F1F8-1F1F7","j":["flag","sr","nation","country","banner","suriname"]},"flag-south-sudan":{"a":"Flag: South Sudan","b":"1F1F8-1F1F8","j":["flag","south","sd","nation","country","banner","south_sudan"]},"flag-so-tom--prncipe":{"a":"Flag: São Tomé & Príncipe","b":"1F1F8-1F1F9","j":["flag","flag_sao_tome_principe","sao","tome","principe","nation","country","banner","sao_tome_principe"]},"flag-el-salvador":{"a":"Flag: El Salvador","b":"1F1F8-1F1FB","j":["flag","el","salvador","nation","country","banner","el_salvador"]},"flag-sint-maarten":{"a":"Flag: Sint Maarten","b":"1F1F8-1F1FD","j":["flag","sint","maarten","dutch","nation","country","banner","sint_maarten"]},"flag-syria":{"a":"Flag: Syria","b":"1F1F8-1F1FE","j":["flag","syrian","arab","republic","nation","country","banner","syria"]},"flag-eswatini":{"a":"Flag: Eswatini","b":"1F1F8-1F1FF","j":["flag","sz","nation","country","banner","eswatini"]},"flag-tristan-da-cunha":{"a":"Flag: Tristan Da Cunha","b":"1F1F9-1F1E6","j":["flag"]},"flag-turks--caicos-islands":{"a":"Flag: Turks & Caicos Islands","b":"1F1F9-1F1E8","j":["flag","flag_turks_caicos_islands","turks","caicos","islands","nation","country","banner","turks_caicos_islands"]},"flag-chad":{"a":"Flag: Chad","b":"1F1F9-1F1E9","j":["flag","td","nation","country","banner","chad"]},"flag-french-southern-territories":{"a":"Flag: French Southern Territories","b":"1F1F9-1F1EB","j":["flag","french","southern","territories","nation","country","banner","french_southern_territories"]},"flag-togo":{"a":"Flag: Togo","b":"1F1F9-1F1EC","j":["flag","tg","nation","country","banner","togo"]},"flag-thailand":{"a":"Flag: Thailand","b":"1F1F9-1F1ED","j":["flag","th","nation","country","banner","thailand"]},"flag-tajikistan":{"a":"Flag: Tajikistan","b":"1F1F9-1F1EF","j":["flag","tj","nation","country","banner","tajikistan"]},"flag-tokelau":{"a":"Flag: Tokelau","b":"1F1F9-1F1F0","j":["flag","tk","nation","country","banner","tokelau"]},"flag-timorleste":{"a":"Flag: Timor-Leste","b":"1F1F9-1F1F1","j":["flag","flag_timor_leste","timor","leste","nation","country","banner","timor_leste"]},"flag-turkmenistan":{"a":"Flag: Turkmenistan","b":"1F1F9-1F1F2","j":["flag","nation","country","banner","turkmenistan"]},"flag-tunisia":{"a":"Flag: Tunisia","b":"1F1F9-1F1F3","j":["flag","tn","nation","country","banner","tunisia"]},"flag-tonga":{"a":"Flag: Tonga","b":"1F1F9-1F1F4","j":["flag","to","nation","country","banner","tonga"]},"flag-turkey":{"a":"Flag: Turkey","b":"1F1F9-1F1F7","j":["flag","turkey","nation","country","banner"]},"flag-trinidad--tobago":{"a":"Flag: Trinidad & Tobago","b":"1F1F9-1F1F9","j":["flag","flag_trinidad_tobago","trinidad","tobago","nation","country","banner","trinidad_tobago"]},"flag-tuvalu":{"a":"Flag: Tuvalu","b":"1F1F9-1F1FB","j":["flag","nation","country","banner","tuvalu"]},"flag-taiwan":{"a":"Flag: Taiwan","b":"1F1F9-1F1FC","j":["flag","tw","nation","country","banner","taiwan"]},"flag-tanzania":{"a":"Flag: Tanzania","b":"1F1F9-1F1FF","j":["flag","tanzania","united","republic","nation","country","banner"]},"flag-ukraine":{"a":"Flag: Ukraine","b":"1F1FA-1F1E6","j":["flag","ua","nation","country","banner","ukraine"]},"flag-uganda":{"a":"Flag: Uganda","b":"1F1FA-1F1EC","j":["flag","ug","nation","country","banner","uganda"]},"flag-us-outlying-islands":{"a":"Flag: U.S. Outlying Islands","b":"1F1FA-1F1F2","j":["flag","flag_u_s_outlying_islands"]},"flag-united-nations":{"a":"Flag: United Nations","b":"1F1FA-1F1F3","j":["flag","un","banner"]},"flag-united-states":{"a":"Flag: United States","b":"1F1FA-1F1F8","j":["flag","united","states","america","nation","country","banner","united_states"]},"flag-uruguay":{"a":"Flag: Uruguay","b":"1F1FA-1F1FE","j":["flag","uy","nation","country","banner","uruguay"]},"flag-uzbekistan":{"a":"Flag: Uzbekistan","b":"1F1FA-1F1FF","j":["flag","uz","nation","country","banner","uzbekistan"]},"flag-vatican-city":{"a":"Flag: Vatican City","b":"1F1FB-1F1E6","j":["flag","vatican","city","nation","country","banner","vatican_city"]},"flag-st-vincent--grenadines":{"a":"Flag: St. Vincent & Grenadines","b":"1F1FB-1F1E8","j":["flag","flag_st_vincent_grenadines","saint","vincent","grenadines","nation","country","banner","st_vincent_grenadines"]},"flag-venezuela":{"a":"Flag: Venezuela","b":"1F1FB-1F1EA","j":["flag","ve","bolivarian","republic","nation","country","banner","venezuela"]},"flag-british-virgin-islands":{"a":"Flag: British Virgin Islands","b":"1F1FB-1F1EC","j":["flag","british","virgin","islands","bvi","nation","country","banner","british_virgin_islands"]},"flag-us-virgin-islands":{"a":"Flag: U.S. Virgin Islands","b":"1F1FB-1F1EE","j":["flag","flag_u_s_virgin_islands","virgin","islands","us","nation","country","banner","u_s_virgin_islands"]},"flag-vietnam":{"a":"Flag: Vietnam","b":"1F1FB-1F1F3","j":["flag","viet","nam","nation","country","banner","vietnam"]},"flag-vanuatu":{"a":"Flag: Vanuatu","b":"1F1FB-1F1FA","j":["flag","vu","nation","country","banner","vanuatu"]},"flag-wallis--futuna":{"a":"Flag: Wallis & Futuna","b":"1F1FC-1F1EB","j":["flag","flag_wallis_futuna","wallis","futuna","nation","country","banner","wallis_futuna"]},"flag-samoa":{"a":"Flag: Samoa","b":"1F1FC-1F1F8","j":["flag","ws","nation","country","banner","samoa"]},"flag-kosovo":{"a":"Flag: Kosovo","b":"1F1FD-1F1F0","j":["flag","xk","nation","country","banner","kosovo"]},"flag-yemen":{"a":"Flag: Yemen","b":"1F1FE-1F1EA","j":["flag","ye","nation","country","banner","yemen"]},"flag-mayotte":{"a":"Flag: Mayotte","b":"1F1FE-1F1F9","j":["flag","yt","nation","country","banner","mayotte"]},"flag-south-africa":{"a":"Flag: South Africa","b":"1F1FF-1F1E6","j":["flag","south","africa","nation","country","banner","south_africa"]},"flag-zambia":{"a":"Flag: Zambia","b":"1F1FF-1F1F2","j":["flag","zm","nation","country","banner","zambia"]},"flag-zimbabwe":{"a":"Flag: Zimbabwe","b":"1F1FF-1F1FC","j":["flag","zw","nation","country","banner","zimbabwe"]},"flag-england":{"a":"Flag: England","b":"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F","j":["flag","english"]},"flag-scotland":{"a":"Flag: Scotland","b":"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F","j":["flag","scottish"]},"flag-wales":{"a":"Flag: Wales","b":"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F","j":["flag","welsh"]}},"aliases":{}}
\ No newline at end of file
+{"compressed":true,"categories":[{"id":"smileys_&_emotion","name":"Smileys & Emotion","emojis":["grinning-face","grinning-face-with-big-eyes","grinning-face-with-smiling-eyes","beaming-face-with-smiling-eyes","grinning-squinting-face","grinning-face-with-sweat","rolling-on-the-floor-laughing","face-with-tears-of-joy","slightly-smiling-face","upsidedown-face","melting-face","winking-face","smiling-face-with-smiling-eyes","smiling-face-with-halo","smiling-face-with-hearts","smiling-face-with-hearteyes","starstruck","face-blowing-a-kiss","kissing-face","smiling-face","kissing-face-with-closed-eyes","kissing-face-with-smiling-eyes","smiling-face-with-tear","face-savoring-food","face-with-tongue","winking-face-with-tongue","zany-face","squinting-face-with-tongue","moneymouth-face","smiling-face-with-open-hands","face-with-hand-over-mouth","face-with-open-eyes-and-hand-over-mouth","face-with-peeking-eye","shushing-face","thinking-face","saluting-face","zippermouth-face","face-with-raised-eyebrow","neutral-face","expressionless-face","face-without-mouth","dotted-line-face","face-in-clouds","smirking-face","unamused-face","face-with-rolling-eyes","grimacing-face","face-exhaling","lying-face","shaking-face","relieved-face","pensive-face","sleepy-face","drooling-face","sleeping-face","face-with-medical-mask","face-with-thermometer","face-with-headbandage","nauseated-face","face-vomiting","sneezing-face","hot-face","cold-face","woozy-face","face-with-crossedout-eyes","face-with-spiral-eyes","exploding-head","cowboy-hat-face","partying-face","disguised-face","smiling-face-with-sunglasses","nerd-face","face-with-monocle","confused-face","face-with-diagonal-mouth","worried-face","slightly-frowning-face","frowning-face","face-with-open-mouth","hushed-face","astonished-face","flushed-face","pleading-face","face-holding-back-tears","frowning-face-with-open-mouth","anguished-face","fearful-face","anxious-face-with-sweat","sad-but-relieved-face","crying-face","loudly-crying-face","face-screaming-in-fear","confounded-face","persevering-face","disappointed-face","downcast-face-with-sweat","weary-face","tired-face","yawning-face","face-with-steam-from-nose","enraged-face","angry-face","face-with-symbols-on-mouth","smiling-face-with-horns","angry-face-with-horns","skull","skull-and-crossbones","pile-of-poo","clown-face","ogre","goblin","ghost","alien","alien-monster","robot","grinning-cat","grinning-cat-with-smiling-eyes","cat-with-tears-of-joy","smiling-cat-with-hearteyes","cat-with-wry-smile","kissing-cat","weary-cat","crying-cat","pouting-cat","seenoevil-monkey","hearnoevil-monkey","speaknoevil-monkey","love-letter","heart-with-arrow","heart-with-ribbon","sparkling-heart","growing-heart","beating-heart","revolving-hearts","two-hearts","heart-decoration","heart-exclamation","broken-heart","heart-on-fire","mending-heart","red-heart","pink-heart","orange-heart","yellow-heart","green-heart","blue-heart","light-blue-heart","purple-heart","brown-heart","black-heart","grey-heart","white-heart","kiss-mark","hundred-points","anger-symbol","collision","dizzy","sweat-droplets","dashing-away","hole","speech-balloon","eye-in-speech-bubble","left-speech-bubble","right-anger-bubble","thought-balloon","zzz"]},{"id":"people_&_body","name":"People & Body","emojis":["waving-hand","raised-back-of-hand","hand-with-fingers-splayed","raised-hand","vulcan-salute","rightwards-hand","leftwards-hand","palm-down-hand","palm-up-hand","leftwards-pushing-hand","rightwards-pushing-hand","ok-hand","pinched-fingers","pinching-hand","victory-hand","crossed-fingers","hand-with-index-finger-and-thumb-crossed","loveyou-gesture","sign-of-the-horns","call-me-hand","backhand-index-pointing-left","backhand-index-pointing-right","backhand-index-pointing-up","middle-finger","backhand-index-pointing-down","index-pointing-up","index-pointing-at-the-viewer","thumbs-up","thumbs-down","raised-fist","oncoming-fist","leftfacing-fist","rightfacing-fist","clapping-hands","raising-hands","heart-hands","open-hands","palms-up-together","handshake","folded-hands","writing-hand","nail-polish","selfie","flexed-biceps","mechanical-arm","mechanical-leg","leg","foot","ear","ear-with-hearing-aid","nose","brain","anatomical-heart","lungs","tooth","bone","eyes","eye","tongue","mouth","biting-lip","baby","child","boy","girl","person","person-blond-hair","man","person-beard","man-beard","woman-beard","man-red-hair","man-curly-hair","man-white-hair","man-bald","woman","woman-red-hair","person-red-hair","woman-curly-hair","person-curly-hair","woman-white-hair","person-white-hair","woman-bald","person-bald","woman-blond-hair","man-blond-hair","older-person","old-man","old-woman","person-frowning","man-frowning","woman-frowning","person-pouting","man-pouting","woman-pouting","person-gesturing-no","man-gesturing-no","woman-gesturing-no","person-gesturing-ok","man-gesturing-ok","woman-gesturing-ok","person-tipping-hand","man-tipping-hand","woman-tipping-hand","person-raising-hand","man-raising-hand","woman-raising-hand","deaf-person","deaf-man","deaf-woman","person-bowing","man-bowing","woman-bowing","person-facepalming","man-facepalming","woman-facepalming","person-shrugging","man-shrugging","woman-shrugging","health-worker","man-health-worker","woman-health-worker","student","man-student","woman-student","teacher","man-teacher","woman-teacher","judge","man-judge","woman-judge","farmer","man-farmer","woman-farmer","cook","man-cook","woman-cook","mechanic","man-mechanic","woman-mechanic","factory-worker","man-factory-worker","woman-factory-worker","office-worker","man-office-worker","woman-office-worker","scientist","man-scientist","woman-scientist","technologist","man-technologist","woman-technologist","singer","man-singer","woman-singer","artist","man-artist","woman-artist","pilot","man-pilot","woman-pilot","astronaut","man-astronaut","woman-astronaut","firefighter","man-firefighter","woman-firefighter","police-officer","man-police-officer","woman-police-officer","detective","man-detective","woman-detective","guard","man-guard","woman-guard","ninja","construction-worker","man-construction-worker","woman-construction-worker","person-with-crown","prince","princess","person-wearing-turban","man-wearing-turban","woman-wearing-turban","person-with-skullcap","woman-with-headscarf","person-in-tuxedo","man-in-tuxedo","woman-in-tuxedo","person-with-veil","man-with-veil","woman-with-veil","pregnant-woman","pregnant-man","pregnant-person","breastfeeding","woman-feeding-baby","man-feeding-baby","person-feeding-baby","baby-angel","santa-claus","mrs-claus","mx-claus","superhero","man-superhero","woman-superhero","supervillain","man-supervillain","woman-supervillain","mage","man-mage","woman-mage","fairy","man-fairy","woman-fairy","vampire","man-vampire","woman-vampire","merperson","merman","mermaid","elf","man-elf","woman-elf","genie","man-genie","woman-genie","zombie","man-zombie","woman-zombie","troll","person-getting-massage","man-getting-massage","woman-getting-massage","person-getting-haircut","man-getting-haircut","woman-getting-haircut","person-walking","man-walking","woman-walking","person-standing","man-standing","woman-standing","person-kneeling","man-kneeling","woman-kneeling","person-with-white-cane","man-with-white-cane","woman-with-white-cane","person-in-motorized-wheelchair","man-in-motorized-wheelchair","woman-in-motorized-wheelchair","person-in-manual-wheelchair","man-in-manual-wheelchair","woman-in-manual-wheelchair","person-running","man-running","woman-running","woman-dancing","man-dancing","person-in-suit-levitating","people-with-bunny-ears","men-with-bunny-ears","women-with-bunny-ears","person-in-steamy-room","man-in-steamy-room","woman-in-steamy-room","person-climbing","man-climbing","woman-climbing","person-fencing","horse-racing","skier","snowboarder","person-golfing","man-golfing","woman-golfing","person-surfing","man-surfing","woman-surfing","person-rowing-boat","man-rowing-boat","woman-rowing-boat","person-swimming","man-swimming","woman-swimming","person-bouncing-ball","man-bouncing-ball","woman-bouncing-ball","person-lifting-weights","man-lifting-weights","woman-lifting-weights","person-biking","man-biking","woman-biking","person-mountain-biking","man-mountain-biking","woman-mountain-biking","person-cartwheeling","man-cartwheeling","woman-cartwheeling","people-wrestling","men-wrestling","women-wrestling","person-playing-water-polo","man-playing-water-polo","woman-playing-water-polo","person-playing-handball","man-playing-handball","woman-playing-handball","person-juggling","man-juggling","woman-juggling","person-in-lotus-position","man-in-lotus-position","woman-in-lotus-position","person-taking-bath","person-in-bed","people-holding-hands","women-holding-hands","woman-and-man-holding-hands","men-holding-hands","kiss","kiss-woman-man","kiss-man-man","kiss-woman-woman","couple-with-heart","couple-with-heart-woman-man","couple-with-heart-man-man","couple-with-heart-woman-woman","family","family-man-woman-boy","family-man-woman-girl","family-man-woman-girl-boy","family-man-woman-boy-boy","family-man-woman-girl-girl","family-man-man-boy","family-man-man-girl","family-man-man-girl-boy","family-man-man-boy-boy","family-man-man-girl-girl","family-woman-woman-boy","family-woman-woman-girl","family-woman-woman-girl-boy","family-woman-woman-boy-boy","family-woman-woman-girl-girl","family-man-boy","family-man-boy-boy","family-man-girl","family-man-girl-boy","family-man-girl-girl","family-woman-boy","family-woman-boy-boy","family-woman-girl","family-woman-girl-boy","family-woman-girl-girl","speaking-head","bust-in-silhouette","busts-in-silhouette","people-hugging","footprints"]},{"id":"animals_&_nature","name":"Animals & Nature","emojis":["monkey-face","monkey","gorilla","orangutan","dog-face","dog","guide-dog","service-dog","poodle","wolf","fox","raccoon","cat-face","cat","black-cat","lion","tiger-face","tiger","leopard","horse-face","moose","donkey","horse","unicorn","zebra","deer","bison","cow-face","ox","water-buffalo","cow","pig-face","pig","boar","pig-nose","ram","ewe","goat","camel","twohump-camel","llama","giraffe","elephant","mammoth","rhinoceros","hippopotamus","mouse-face","mouse","rat","hamster","rabbit-face","rabbit","chipmunk","beaver","hedgehog","bat","bear","polar-bear","koala","panda","sloth","otter","skunk","kangaroo","badger","paw-prints","turkey","chicken","rooster","hatching-chick","baby-chick","frontfacing-baby-chick","bird","penguin","dove","eagle","duck","swan","owl","dodo","feather","flamingo","peacock","parrot","wing","black-bird","goose","frog","crocodile","turtle","lizard","snake","dragon-face","dragon","sauropod","trex","spouting-whale","whale","dolphin","seal","fish","tropical-fish","blowfish","shark","octopus","spiral-shell","coral","jellyfish","snail","butterfly","bug","ant","honeybee","beetle","lady-beetle","cricket","cockroach","spider","spider-web","scorpion","mosquito","fly","worm","microbe","bouquet","cherry-blossom","white-flower","lotus","rosette","rose","wilted-flower","hibiscus","sunflower","blossom","tulip","hyacinth","seedling","potted-plant","evergreen-tree","deciduous-tree","palm-tree","cactus","sheaf-of-rice","herb","shamrock","four-leaf-clover","maple-leaf","fallen-leaf","leaf-fluttering-in-wind","empty-nest","nest-with-eggs","mushroom"]},{"id":"food_&_drink","name":"Food & Drink","emojis":["grapes","melon","watermelon","tangerine","lemon","banana","pineapple","mango","red-apple","green-apple","pear","peach","cherries","strawberry","blueberries","kiwi-fruit","tomato","olive","coconut","avocado","eggplant","potato","carrot","ear-of-corn","hot-pepper","bell-pepper","cucumber","leafy-green","broccoli","garlic","onion","peanuts","beans","chestnut","ginger-root","pea-pod","bread","croissant","baguette-bread","flatbread","pretzel","bagel","pancakes","waffle","cheese-wedge","meat-on-bone","poultry-leg","cut-of-meat","bacon","hamburger","french-fries","pizza","hot-dog","sandwich","taco","burrito","tamale","stuffed-flatbread","falafel","egg","cooking","shallow-pan-of-food","pot-of-food","fondue","bowl-with-spoon","green-salad","popcorn","butter","salt","canned-food","bento-box","rice-cracker","rice-ball","cooked-rice","curry-rice","steaming-bowl","spaghetti","roasted-sweet-potato","oden","sushi","fried-shrimp","fish-cake-with-swirl","moon-cake","dango","dumpling","fortune-cookie","takeout-box","crab","lobster","shrimp","squid","oyster","soft-ice-cream","shaved-ice","ice-cream","doughnut","cookie","birthday-cake","shortcake","cupcake","pie","chocolate-bar","candy","lollipop","custard","honey-pot","baby-bottle","glass-of-milk","hot-beverage","teapot","teacup-without-handle","sake","bottle-with-popping-cork","wine-glass","cocktail-glass","tropical-drink","beer-mug","clinking-beer-mugs","clinking-glasses","tumbler-glass","pouring-liquid","cup-with-straw","bubble-tea","beverage-box","mate","ice","chopsticks","fork-and-knife-with-plate","fork-and-knife","spoon","kitchen-knife","jar","amphora"]},{"id":"travel_&_places","name":"Travel & Places","emojis":["globe-showing-europeafrica","globe-showing-americas","globe-showing-asiaaustralia","globe-with-meridians","world-map","map-of-japan","compass","snowcapped-mountain","mountain","volcano","mount-fuji","camping","beach-with-umbrella","desert","desert-island","national-park","stadium","classical-building","building-construction","brick","rock","wood","hut","houses","derelict-house","house","house-with-garden","office-building","japanese-post-office","post-office","hospital","bank","hotel","love-hotel","convenience-store","school","department-store","factory","japanese-castle","castle","wedding","tokyo-tower","statue-of-liberty","church","mosque","hindu-temple","synagogue","shinto-shrine","kaaba","fountain","tent","foggy","night-with-stars","cityscape","sunrise-over-mountains","sunrise","cityscape-at-dusk","sunset","bridge-at-night","hot-springs","carousel-horse","playground-slide","ferris-wheel","roller-coaster","barber-pole","circus-tent","locomotive","railway-car","highspeed-train","bullet-train","train","metro","light-rail","station","tram","monorail","mountain-railway","tram-car","bus","oncoming-bus","trolleybus","minibus","ambulance","fire-engine","police-car","oncoming-police-car","taxi","oncoming-taxi","automobile","oncoming-automobile","sport-utility-vehicle","pickup-truck","delivery-truck","articulated-lorry","tractor","racing-car","motorcycle","motor-scooter","manual-wheelchair","motorized-wheelchair","auto-rickshaw","bicycle","kick-scooter","skateboard","roller-skate","bus-stop","motorway","railway-track","oil-drum","fuel-pump","wheel","police-car-light","horizontal-traffic-light","vertical-traffic-light","stop-sign","construction","anchor","ring-buoy","sailboat","canoe","speedboat","passenger-ship","ferry","motor-boat","ship","airplane","small-airplane","airplane-departure","airplane-arrival","parachute","seat","helicopter","suspension-railway","mountain-cableway","aerial-tramway","satellite","rocket","flying-saucer","bellhop-bell","luggage","hourglass-done","hourglass-not-done","watch","alarm-clock","stopwatch","timer-clock","mantelpiece-clock","twelve-oclock","twelvethirty","one-oclock","onethirty","two-oclock","twothirty","three-oclock","threethirty","four-oclock","fourthirty","five-oclock","fivethirty","six-oclock","sixthirty","seven-oclock","seventhirty","eight-oclock","eightthirty","nine-oclock","ninethirty","ten-oclock","tenthirty","eleven-oclock","eleventhirty","new-moon","waxing-crescent-moon","first-quarter-moon","waxing-gibbous-moon","full-moon","waning-gibbous-moon","last-quarter-moon","waning-crescent-moon","crescent-moon","new-moon-face","first-quarter-moon-face","last-quarter-moon-face","thermometer","sun","full-moon-face","sun-with-face","ringed-planet","star","glowing-star","shooting-star","milky-way","cloud","sun-behind-cloud","cloud-with-lightning-and-rain","sun-behind-small-cloud","sun-behind-large-cloud","sun-behind-rain-cloud","cloud-with-rain","cloud-with-snow","cloud-with-lightning","tornado","fog","wind-face","cyclone","rainbow","closed-umbrella","umbrella","umbrella-with-rain-drops","umbrella-on-ground","high-voltage","snowflake","snowman","snowman-without-snow","comet","fire","droplet","water-wave"]},{"id":"activities","name":"Activities","emojis":["jackolantern","christmas-tree","fireworks","sparkler","firecracker","sparkles","balloon","party-popper","confetti-ball","tanabata-tree","pine-decoration","japanese-dolls","carp-streamer","wind-chime","moon-viewing-ceremony","red-envelope","ribbon","wrapped-gift","reminder-ribbon","admission-tickets","ticket","military-medal","trophy","sports-medal","1st-place-medal","2nd-place-medal","3rd-place-medal","soccer-ball","baseball","softball","basketball","volleyball","american-football","rugby-football","tennis","flying-disc","bowling","cricket-game","field-hockey","ice-hockey","lacrosse","ping-pong","badminton","boxing-glove","martial-arts-uniform","goal-net","flag-in-hole","ice-skate","fishing-pole","diving-mask","running-shirt","skis","sled","curling-stone","bullseye","yoyo","kite","water-pistol","pool-8-ball","crystal-ball","magic-wand","video-game","joystick","slot-machine","game-die","puzzle-piece","teddy-bear","piata","mirror-ball","nesting-dolls","spade-suit","heart-suit","diamond-suit","club-suit","chess-pawn","joker","mahjong-red-dragon","flower-playing-cards","performing-arts","framed-picture","artist-palette","thread","sewing-needle","yarn","knot"]},{"id":"objects","name":"Objects","emojis":["glasses","sunglasses","goggles","lab-coat","safety-vest","necktie","tshirt","jeans","scarf","gloves","coat","socks","dress","kimono","sari","onepiece-swimsuit","briefs","shorts","bikini","womans-clothes","folding-hand-fan","purse","handbag","clutch-bag","shopping-bags","backpack","thong-sandal","mans-shoe","running-shoe","hiking-boot","flat-shoe","highheeled-shoe","womans-sandal","ballet-shoes","womans-boot","hair-pick","crown","womans-hat","top-hat","graduation-cap","billed-cap","military-helmet","rescue-workers-helmet","prayer-beads","lipstick","ring","gem-stone","muted-speaker","speaker-low-volume","speaker-medium-volume","speaker-high-volume","loudspeaker","megaphone","postal-horn","bell","bell-with-slash","musical-score","musical-note","musical-notes","studio-microphone","level-slider","control-knobs","microphone","headphone","radio","saxophone","accordion","guitar","musical-keyboard","trumpet","violin","banjo","drum","long-drum","maracas","flute","mobile-phone","mobile-phone-with-arrow","telephone","telephone-receiver","pager","fax-machine","battery","low-battery","electric-plug","laptop","desktop-computer","printer","keyboard","computer-mouse","trackball","computer-disk","floppy-disk","optical-disk","dvd","abacus","movie-camera","film-frames","film-projector","clapper-board","television","camera","camera-with-flash","video-camera","videocassette","magnifying-glass-tilted-left","magnifying-glass-tilted-right","candle","light-bulb","flashlight","red-paper-lantern","diya-lamp","notebook-with-decorative-cover","closed-book","open-book","green-book","blue-book","orange-book","books","notebook","ledger","page-with-curl","scroll","page-facing-up","newspaper","rolledup-newspaper","bookmark-tabs","bookmark","label","money-bag","coin","yen-banknote","dollar-banknote","euro-banknote","pound-banknote","money-with-wings","credit-card","receipt","chart-increasing-with-yen","envelope","email","incoming-envelope","envelope-with-arrow","outbox-tray","inbox-tray","package","closed-mailbox-with-raised-flag","closed-mailbox-with-lowered-flag","open-mailbox-with-raised-flag","open-mailbox-with-lowered-flag","postbox","ballot-box-with-ballot","pencil","black-nib","fountain-pen","pen","paintbrush","crayon","memo","briefcase","file-folder","open-file-folder","card-index-dividers","calendar","tearoff-calendar","spiral-notepad","spiral-calendar","card-index","chart-increasing","chart-decreasing","bar-chart","clipboard","pushpin","round-pushpin","paperclip","linked-paperclips","straight-ruler","triangular-ruler","scissors","card-file-box","file-cabinet","wastebasket","locked","unlocked","locked-with-pen","locked-with-key","key","old-key","hammer","axe","pick","hammer-and-pick","hammer-and-wrench","dagger","crossed-swords","bomb","boomerang","bow-and-arrow","shield","carpentry-saw","wrench","screwdriver","nut-and-bolt","gear","clamp","balance-scale","white-cane","link","chains","hook","toolbox","magnet","ladder","alembic","test-tube","petri-dish","dna","microscope","telescope","satellite-antenna","syringe","drop-of-blood","pill","adhesive-bandage","crutch","stethoscope","xray","door","elevator","mirror","window","bed","couch-and-lamp","chair","toilet","plunger","shower","bathtub","mouse-trap","razor","lotion-bottle","safety-pin","broom","basket","roll-of-paper","bucket","soap","bubbles","toothbrush","sponge","fire-extinguisher","shopping-cart","cigarette","coffin","headstone","funeral-urn","nazar-amulet","hamsa","moai","placard","identification-card"]},{"id":"symbols","name":"Symbols","emojis":["atm-sign","litter-in-bin-sign","potable-water","wheelchair-symbol","mens-room","womens-room","restroom","baby-symbol","water-closet","passport-control","customs","baggage-claim","left-luggage","warning","children-crossing","no-entry","prohibited","no-bicycles","no-smoking","no-littering","nonpotable-water","no-pedestrians","no-mobile-phones","no-one-under-eighteen","radioactive","biohazard","up-arrow","upright-arrow","right-arrow","downright-arrow","down-arrow","downleft-arrow","left-arrow","upleft-arrow","updown-arrow","leftright-arrow","right-arrow-curving-left","left-arrow-curving-right","right-arrow-curving-up","right-arrow-curving-down","clockwise-vertical-arrows","counterclockwise-arrows-button","back-arrow","end-arrow","on-arrow","soon-arrow","top-arrow","place-of-worship","atom-symbol","om","star-of-david","wheel-of-dharma","yin-yang","latin-cross","orthodox-cross","star-and-crescent","peace-symbol","menorah","dotted-sixpointed-star","khanda","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn","aquarius","pisces","ophiuchus","shuffle-tracks-button","repeat-button","repeat-single-button","play-button","fastforward-button","next-track-button","play-or-pause-button","reverse-button","fast-reverse-button","last-track-button","upwards-button","fast-up-button","downwards-button","fast-down-button","pause-button","stop-button","record-button","eject-button","cinema","dim-button","bright-button","antenna-bars","wireless","vibration-mode","mobile-phone-off","female-sign","male-sign","transgender-symbol","multiply","plus","minus","divide","heavy-equals-sign","infinity","double-exclamation-mark","exclamation-question-mark","red-question-mark","white-question-mark","white-exclamation-mark","red-exclamation-mark","wavy-dash","currency-exchange","heavy-dollar-sign","medical-symbol","recycling-symbol","fleurdelis","trident-emblem","name-badge","japanese-symbol-for-beginner","hollow-red-circle","check-mark-button","check-box-with-check","check-mark","cross-mark","cross-mark-button","curly-loop","double-curly-loop","part-alternation-mark","eightspoked-asterisk","eightpointed-star","sparkle","copyright","registered","trade-mark","keycap","keycap","keycap-0","keycap-1","keycap-2","keycap-3","keycap-4","keycap-5","keycap-6","keycap-7","keycap-8","keycap-9","keycap-10","input-latin-uppercase","input-latin-lowercase","input-numbers","input-symbols","input-latin-letters","a-button-blood-type","ab-button-blood-type","b-button-blood-type","cl-button","cool-button","free-button","information","id-button","circled-m","new-button","ng-button","o-button-blood-type","ok-button","p-button","sos-button","up-button","vs-button","japanese-here-button","japanese-service-charge-button","japanese-monthly-amount-button","japanese-not-free-of-charge-button","japanese-reserved-button","japanese-bargain-button","japanese-discount-button","japanese-free-of-charge-button","japanese-prohibited-button","japanese-acceptable-button","japanese-application-button","japanese-passing-grade-button","japanese-vacancy-button","japanese-congratulations-button","japanese-secret-button","japanese-open-for-business-button","japanese-no-vacancy-button","red-circle","orange-circle","yellow-circle","green-circle","blue-circle","purple-circle","brown-circle","black-circle","white-circle","red-square","orange-square","yellow-square","green-square","blue-square","purple-square","brown-square","black-large-square","white-large-square","black-medium-square","white-medium-square","black-mediumsmall-square","white-mediumsmall-square","black-small-square","white-small-square","large-orange-diamond","large-blue-diamond","small-orange-diamond","small-blue-diamond","red-triangle-pointed-up","red-triangle-pointed-down","diamond-with-a-dot","radio-button","white-square-button","black-square-button"]},{"id":"flags","name":"Flags","emojis":["chequered-flag","triangular-flag","crossed-flags","black-flag","white-flag","rainbow-flag","transgender-flag","pirate-flag","flag-ascension-island","flag-andorra","flag-united-arab-emirates","flag-afghanistan","flag-antigua--barbuda","flag-anguilla","flag-albania","flag-armenia","flag-angola","flag-antarctica","flag-argentina","flag-american-samoa","flag-austria","flag-australia","flag-aruba","flag-land-islands","flag-azerbaijan","flag-bosnia--herzegovina","flag-barbados","flag-bangladesh","flag-belgium","flag-burkina-faso","flag-bulgaria","flag-bahrain","flag-burundi","flag-benin","flag-st-barthlemy","flag-bermuda","flag-brunei","flag-bolivia","flag-caribbean-netherlands","flag-brazil","flag-bahamas","flag-bhutan","flag-bouvet-island","flag-botswana","flag-belarus","flag-belize","flag-canada","flag-cocos-keeling-islands","flag-congo--kinshasa","flag-central-african-republic","flag-congo--brazzaville","flag-switzerland","flag-cte-divoire","flag-cook-islands","flag-chile","flag-cameroon","flag-china","flag-colombia","flag-clipperton-island","flag-costa-rica","flag-cuba","flag-cape-verde","flag-curaao","flag-christmas-island","flag-cyprus","flag-czechia","flag-germany","flag-diego-garcia","flag-djibouti","flag-denmark","flag-dominica","flag-dominican-republic","flag-algeria","flag-ceuta--melilla","flag-ecuador","flag-estonia","flag-egypt","flag-western-sahara","flag-eritrea","flag-spain","flag-ethiopia","flag-european-union","flag-finland","flag-fiji","flag-falkland-islands","flag-micronesia","flag-faroe-islands","flag-france","flag-gabon","flag-united-kingdom","flag-grenada","flag-georgia","flag-french-guiana","flag-guernsey","flag-ghana","flag-gibraltar","flag-greenland","flag-gambia","flag-guinea","flag-guadeloupe","flag-equatorial-guinea","flag-greece","flag-south-georgia--south-sandwich-islands","flag-guatemala","flag-guam","flag-guineabissau","flag-guyana","flag-hong-kong-sar-china","flag-heard--mcdonald-islands","flag-honduras","flag-croatia","flag-haiti","flag-hungary","flag-canary-islands","flag-indonesia","flag-ireland","flag-israel","flag-isle-of-man","flag-india","flag-british-indian-ocean-territory","flag-iraq","flag-iran","flag-iceland","flag-italy","flag-jersey","flag-jamaica","flag-jordan","flag-japan","flag-kenya","flag-kyrgyzstan","flag-cambodia","flag-kiribati","flag-comoros","flag-st-kitts--nevis","flag-north-korea","flag-south-korea","flag-kuwait","flag-cayman-islands","flag-kazakhstan","flag-laos","flag-lebanon","flag-st-lucia","flag-liechtenstein","flag-sri-lanka","flag-liberia","flag-lesotho","flag-lithuania","flag-luxembourg","flag-latvia","flag-libya","flag-morocco","flag-monaco","flag-moldova","flag-montenegro","flag-st-martin","flag-madagascar","flag-marshall-islands","flag-north-macedonia","flag-mali","flag-myanmar-burma","flag-mongolia","flag-macao-sar-china","flag-northern-mariana-islands","flag-martinique","flag-mauritania","flag-montserrat","flag-malta","flag-mauritius","flag-maldives","flag-malawi","flag-mexico","flag-malaysia","flag-mozambique","flag-namibia","flag-new-caledonia","flag-niger","flag-norfolk-island","flag-nigeria","flag-nicaragua","flag-netherlands","flag-norway","flag-nepal","flag-nauru","flag-niue","flag-new-zealand","flag-oman","flag-panama","flag-peru","flag-french-polynesia","flag-papua-new-guinea","flag-philippines","flag-pakistan","flag-poland","flag-st-pierre--miquelon","flag-pitcairn-islands","flag-puerto-rico","flag-palestinian-territories","flag-portugal","flag-palau","flag-paraguay","flag-qatar","flag-runion","flag-romania","flag-serbia","flag-russia","flag-rwanda","flag-saudi-arabia","flag-solomon-islands","flag-seychelles","flag-sudan","flag-sweden","flag-singapore","flag-st-helena","flag-slovenia","flag-svalbard--jan-mayen","flag-slovakia","flag-sierra-leone","flag-san-marino","flag-senegal","flag-somalia","flag-suriname","flag-south-sudan","flag-so-tom--prncipe","flag-el-salvador","flag-sint-maarten","flag-syria","flag-eswatini","flag-tristan-da-cunha","flag-turks--caicos-islands","flag-chad","flag-french-southern-territories","flag-togo","flag-thailand","flag-tajikistan","flag-tokelau","flag-timorleste","flag-turkmenistan","flag-tunisia","flag-tonga","flag-turkey","flag-trinidad--tobago","flag-tuvalu","flag-taiwan","flag-tanzania","flag-ukraine","flag-uganda","flag-us-outlying-islands","flag-united-nations","flag-united-states","flag-uruguay","flag-uzbekistan","flag-vatican-city","flag-st-vincent--grenadines","flag-venezuela","flag-british-virgin-islands","flag-us-virgin-islands","flag-vietnam","flag-vanuatu","flag-wallis--futuna","flag-samoa","flag-kosovo","flag-yemen","flag-mayotte","flag-south-africa","flag-zambia","flag-zimbabwe","flag-england","flag-scotland","flag-wales"]}],"emojis":{"grinning-face":{"a":"Grinning Face","b":"1F600","j":["face","grin","smile","happy","joy",":D"]},"grinning-face-with-big-eyes":{"a":"Grinning Face with Big Eyes","b":"1F603","j":["face","mouth","open","smile","happy","joy","haha",":D",":)","funny"]},"grinning-face-with-smiling-eyes":{"a":"Grinning Face with Smiling Eyes","b":"1F604","j":["eye","face","mouth","open","smile","happy","joy","funny","haha","laugh","like",":D",":)"]},"beaming-face-with-smiling-eyes":{"a":"Beaming Face with Smiling Eyes","b":"1F601","j":["eye","face","grin","smile","happy","joy","kawaii"]},"grinning-squinting-face":{"a":"Grinning Squinting Face","b":"1F606","j":["face","laugh","mouth","satisfied","smile","happy","joy","lol","haha","glad","XD"]},"grinning-face-with-sweat":{"a":"Grinning Face with Sweat","b":"1F605","j":["cold","face","open","smile","sweat","hot","happy","laugh","relief"]},"rolling-on-the-floor-laughing":{"a":"Rolling on the Floor Laughing","b":"1F923","j":["face","floor","laugh","rofl","rolling","rotfl","laughing","lol","haha"]},"face-with-tears-of-joy":{"a":"Face with Tears of Joy","b":"1F602","j":["face","joy","laugh","tear","cry","tears","weep","happy","happytears","haha"]},"slightly-smiling-face":{"a":"Slightly Smiling Face","b":"1F642","j":["face","smile"]},"upsidedown-face":{"a":"Upside-Down Face","b":"1F643","j":["face","upside-down","upside_down_face","flipped","silly","smile"]},"melting-face":{"a":"Melting Face","b":"1FAE0","j":["disappear","dissolve","liquid","melt","hot","heat"]},"winking-face":{"a":"Winking Face","b":"1F609","j":["face","wink","happy","mischievous","secret",";)","smile","eye"]},"smiling-face-with-smiling-eyes":{"a":"Smiling Face with Smiling Eyes","b":"1F60A","j":["blush","eye","face","smile","happy","flushed","crush","embarrassed","shy","joy"]},"smiling-face-with-halo":{"a":"Smiling Face with Halo","b":"1F607","j":["angel","face","fantasy","halo","innocent","heaven"]},"smiling-face-with-hearts":{"a":"Smiling Face with Hearts","b":"1F970","j":["adore","crush","hearts","in love","face","love","like","affection","valentines","infatuation"]},"smiling-face-with-hearteyes":{"a":"Smiling Face with Heart-Eyes","b":"1F60D","j":["eye","face","love","smile","smiling face with heart-eyes","smiling_face_with_heart_eyes","like","affection","valentines","infatuation","crush","heart"]},"starstruck":{"a":"Star-Struck","b":"1F929","j":["eyes","face","grinning","star","star-struck","starry-eyed","star_struck","smile","starry"]},"face-blowing-a-kiss":{"a":"Face Blowing a Kiss","b":"1F618","j":["face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face":{"a":"Kissing Face","b":"1F617","j":["face","kiss","love","like","3","valentines","infatuation"]},"smiling-face":{"a":"Smiling Face","b":"263A","j":["face","outlined","relaxed","smile","blush","massage","happiness"]},"kissing-face-with-closed-eyes":{"a":"Kissing Face with Closed Eyes","b":"1F61A","j":["closed","eye","face","kiss","love","like","affection","valentines","infatuation"]},"kissing-face-with-smiling-eyes":{"a":"Kissing Face with Smiling Eyes","b":"1F619","j":["eye","face","kiss","smile","affection","valentines","infatuation"]},"smiling-face-with-tear":{"a":"Smiling Face with Tear","b":"1F972","j":["grateful","proud","relieved","smiling","tear","touched","sad","cry","pretend"]},"face-savoring-food":{"a":"Face Savoring Food","b":"1F60B","j":["delicious","face","savouring","smile","yum","happy","joy","tongue","silly","yummy","nom"]},"face-with-tongue":{"a":"Face with Tongue","b":"1F61B","j":["face","tongue","prank","childish","playful","mischievous","smile"]},"winking-face-with-tongue":{"a":"Winking Face with Tongue","b":"1F61C","j":["eye","face","joke","tongue","wink","prank","childish","playful","mischievous","smile"]},"zany-face":{"a":"Zany Face","b":"1F92A","j":["eye","goofy","large","small","face","crazy"]},"squinting-face-with-tongue":{"a":"Squinting Face with Tongue","b":"1F61D","j":["eye","face","horrible","taste","tongue","prank","playful","mischievous","smile"]},"moneymouth-face":{"a":"Money-Mouth Face","b":"1F911","j":["face","money","money-mouth face","mouth","money_mouth_face","rich","dollar"]},"smiling-face-with-open-hands":{"a":"Smiling Face with Open Hands","b":"1F917","j":["face","hug","hugging","open hands","smiling face","hugging_face","smile"]},"face-with-hand-over-mouth":{"a":"Face with Hand over Mouth","b":"1F92D","j":["whoops","shock","sudden realization","surprise","face"]},"face-with-open-eyes-and-hand-over-mouth":{"a":"Face with Open Eyes and Hand over Mouth","b":"1FAE2","j":["amazement","awe","disbelief","embarrass","scared","surprise","silence","secret","shock"]},"face-with-peeking-eye":{"a":"Face with Peeking Eye","b":"1FAE3","j":["captivated","peep","stare","scared","frightening","embarrassing","shy"]},"shushing-face":{"a":"Shushing Face","b":"1F92B","j":["quiet","shush","face","shhh"]},"thinking-face":{"a":"Thinking Face","b":"1F914","j":["face","thinking","hmmm","think","consider"]},"saluting-face":{"a":"Saluting Face","b":"1FAE1","j":["OK","salute","sunny","troops","yes","respect"]},"zippermouth-face":{"a":"Zipper-Mouth Face","b":"1F910","j":["face","mouth","zipper","zipper-mouth face","zipper_mouth_face","sealed","secret"]},"face-with-raised-eyebrow":{"a":"Face with Raised Eyebrow","b":"1F928","j":["distrust","skeptic","disapproval","disbelief","mild surprise","scepticism","face","surprise"]},"neutral-face":{"a":"Neutral Face","b":"1F610","j":["deadpan","face","meh","neutral","indifference",":|"]},"expressionless-face":{"a":"Expressionless Face","b":"1F611","j":["expressionless","face","inexpressive","meh","unexpressive","indifferent","-_-","deadpan"]},"face-without-mouth":{"a":"Face Without Mouth","b":"1F636","j":["face","mouth","quiet","silent","hellokitty"]},"dotted-line-face":{"a":"Dotted Line Face","b":"1FAE5","j":["depressed","disappear","hide","introvert","invisible","lonely","isolation","depression"]},"face-in-clouds":{"a":"Face in Clouds","b":"1F636-200D-1F32B-FE0F","j":["absentminded","face in the fog","head in clouds","shower","steam","dream"]},"smirking-face":{"a":"Smirking Face","b":"1F60F","j":["face","smirk","smile","mean","prank","smug","sarcasm"]},"unamused-face":{"a":"Unamused Face","b":"1F612","j":["face","unamused","unhappy","indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"]},"face-with-rolling-eyes":{"a":"Face with Rolling Eyes","b":"1F644","j":["eyeroll","eyes","face","rolling","frustrated"]},"grimacing-face":{"a":"Grimacing Face","b":"1F62C","j":["face","grimace","teeth"]},"face-exhaling":{"a":"Face Exhaling","b":"1F62E-200D-1F4A8","j":["exhale","gasp","groan","relief","whisper","whistle","relieve","tired","sigh"]},"lying-face":{"a":"Lying Face","b":"1F925","j":["face","lie","pinocchio"]},"shaking-face":{"a":"⊛ Shaking Face","b":"1FAE8","j":["earthquake","face","shaking","shock","vibrate"]},"relieved-face":{"a":"Relieved Face","b":"1F60C","j":["face","relieved","relaxed","phew","massage","happiness"]},"pensive-face":{"a":"Pensive Face","b":"1F614","j":["dejected","face","pensive","sad","depressed","upset"]},"sleepy-face":{"a":"Sleepy Face","b":"1F62A","j":["face","good night","sleep","tired","rest","nap"]},"drooling-face":{"a":"Drooling Face","b":"1F924","j":["drooling","face"]},"sleeping-face":{"a":"Sleeping Face","b":"1F634","j":["face","good night","sleep","ZZZ","tired","sleepy","night","zzz"]},"face-with-medical-mask":{"a":"Face with Medical Mask","b":"1F637","j":["cold","doctor","face","mask","sick","ill","disease","covid"]},"face-with-thermometer":{"a":"Face with Thermometer","b":"1F912","j":["face","ill","sick","thermometer","temperature","cold","fever","covid"]},"face-with-headbandage":{"a":"Face with Head-Bandage","b":"1F915","j":["bandage","face","face with head-bandage","hurt","injury","face_with_head_bandage","injured","clumsy"]},"nauseated-face":{"a":"Nauseated Face","b":"1F922","j":["face","nauseated","vomit","gross","green","sick","throw up","ill"]},"face-vomiting":{"a":"Face Vomiting","b":"1F92E","j":["puke","sick","vomit","face"]},"sneezing-face":{"a":"Sneezing Face","b":"1F927","j":["face","gesundheit","sneeze","sick","allergy"]},"hot-face":{"a":"Hot Face","b":"1F975","j":["feverish","heat stroke","hot","red-faced","sweating","face","heat","red"]},"cold-face":{"a":"Cold Face","b":"1F976","j":["blue-faced","cold","freezing","frostbite","icicles","face","blue","frozen"]},"woozy-face":{"a":"Woozy Face","b":"1F974","j":["dizzy","intoxicated","tipsy","uneven eyes","wavy mouth","face","wavy"]},"face-with-crossedout-eyes":{"a":"Face with Crossed-out Eyes","b":"1F635","j":["crossed-out eyes","dead","face","face with crossed-out eyes","knocked out","dizzy_face","spent","unconscious","xox","dizzy"]},"face-with-spiral-eyes":{"a":"Face with Spiral Eyes","b":"1F635-200D-1F4AB","j":["dizzy","hypnotized","spiral","trouble","whoa","sick","ill","confused","nauseous","nausea"]},"exploding-head":{"a":"Exploding Head","b":"1F92F","j":["mind blown","shocked","face","mind","blown"]},"cowboy-hat-face":{"a":"Cowboy Hat Face","b":"1F920","j":["cowboy","cowgirl","face","hat"]},"partying-face":{"a":"Partying Face","b":"1F973","j":["celebration","hat","horn","party","face","woohoo"]},"disguised-face":{"a":"Disguised Face","b":"1F978","j":["disguise","face","glasses","incognito","nose","pretent","brows","moustache"]},"smiling-face-with-sunglasses":{"a":"Smiling Face with Sunglasses","b":"1F60E","j":["bright","cool","face","sun","sunglasses","smile","summer","beach","sunglass"]},"nerd-face":{"a":"Nerd Face","b":"1F913","j":["face","geek","nerd","nerdy","dork"]},"face-with-monocle":{"a":"Face with Monocle","b":"1F9D0","j":["face","monocle","stuffy","wealthy"]},"confused-face":{"a":"Confused Face","b":"1F615","j":["confused","face","meh","indifference","huh","weird","hmmm",":/"]},"face-with-diagonal-mouth":{"a":"Face with Diagonal Mouth","b":"1FAE4","j":["disappointed","meh","skeptical","unsure","skeptic","confuse","frustrated","indifferent"]},"worried-face":{"a":"Worried Face","b":"1F61F","j":["face","worried","concern","nervous",":("]},"slightly-frowning-face":{"a":"Slightly Frowning Face","b":"1F641","j":["face","frown","frowning","disappointed","sad","upset"]},"frowning-face":{"a":"Frowning Face","b":"2639","j":["face","frown","sad","upset"]},"face-with-open-mouth":{"a":"Face with Open Mouth","b":"1F62E","j":["face","mouth","open","sympathy","surprise","impressed","wow","whoa",":O"]},"hushed-face":{"a":"Hushed Face","b":"1F62F","j":["face","hushed","stunned","surprised","woo","shh"]},"astonished-face":{"a":"Astonished Face","b":"1F632","j":["astonished","face","shocked","totally","xox","surprised","poisoned"]},"flushed-face":{"a":"Flushed Face","b":"1F633","j":["dazed","face","flushed","blush","shy","flattered"]},"pleading-face":{"a":"Pleading Face","b":"1F97A","j":["begging","mercy","puppy eyes","face"]},"face-holding-back-tears":{"a":"Face Holding Back Tears","b":"1F979","j":["angry","cry","proud","resist","sad","touched","gratitude"]},"frowning-face-with-open-mouth":{"a":"Frowning Face with Open Mouth","b":"1F626","j":["face","frown","mouth","open","aw","what"]},"anguished-face":{"a":"Anguished Face","b":"1F627","j":["anguished","face","stunned","nervous"]},"fearful-face":{"a":"Fearful Face","b":"1F628","j":["face","fear","fearful","scared","terrified","nervous","oops","huh"]},"anxious-face-with-sweat":{"a":"Anxious Face with Sweat","b":"1F630","j":["blue","cold","face","rushed","sweat","nervous"]},"sad-but-relieved-face":{"a":"Sad but Relieved Face","b":"1F625","j":["disappointed","face","relieved","whew","phew","sweat","nervous"]},"crying-face":{"a":"Crying Face","b":"1F622","j":["cry","face","sad","tear","tears","depressed","upset",":'("]},"loudly-crying-face":{"a":"Loudly Crying Face","b":"1F62D","j":["cry","face","sad","sob","tear","tears","upset","depressed"]},"face-screaming-in-fear":{"a":"Face Screaming in Fear","b":"1F631","j":["face","fear","munch","scared","scream","omg"]},"confounded-face":{"a":"Confounded Face","b":"1F616","j":["confounded","face","confused","sick","unwell","oops",":S"]},"persevering-face":{"a":"Persevering Face","b":"1F623","j":["face","persevere","sick","no","upset","oops"]},"disappointed-face":{"a":"Disappointed Face","b":"1F61E","j":["disappointed","face","sad","upset","depressed",":("]},"downcast-face-with-sweat":{"a":"Downcast Face with Sweat","b":"1F613","j":["cold","face","sweat","hot","sad","tired","exercise"]},"weary-face":{"a":"Weary Face","b":"1F629","j":["face","tired","weary","sleepy","sad","frustrated","upset"]},"tired-face":{"a":"Tired Face","b":"1F62B","j":["face","tired","sick","whine","upset","frustrated"]},"yawning-face":{"a":"Yawning Face","b":"1F971","j":["bored","tired","yawn","sleepy"]},"face-with-steam-from-nose":{"a":"Face with Steam From Nose","b":"1F624","j":["face","triumph","won","gas","phew","proud","pride"]},"enraged-face":{"a":"Enraged Face","b":"1F621","j":["angry","enraged","face","mad","pouting","rage","red","pouting_face","hate","despise"]},"angry-face":{"a":"Angry Face","b":"1F620","j":["anger","angry","face","mad","annoyed","frustrated"]},"face-with-symbols-on-mouth":{"a":"Face with Symbols on Mouth","b":"1F92C","j":["swearing","cursing","face","cussing","profanity","expletive"]},"smiling-face-with-horns":{"a":"Smiling Face with Horns","b":"1F608","j":["face","fairy tale","fantasy","horns","smile","devil"]},"angry-face-with-horns":{"a":"Angry Face with Horns","b":"1F47F","j":["demon","devil","face","fantasy","imp","angry","horns"]},"skull":{"a":"Skull","b":"1F480","j":["death","face","fairy tale","monster","dead","skeleton","creepy"]},"skull-and-crossbones":{"a":"Skull and Crossbones","b":"2620","j":["crossbones","death","face","monster","skull","poison","danger","deadly","scary","pirate","evil"]},"pile-of-poo":{"a":"Pile of Poo","b":"1F4A9","j":["dung","face","monster","poo","poop","hankey","shitface","fail","turd","shit"]},"clown-face":{"a":"Clown Face","b":"1F921","j":["clown","face"]},"ogre":{"a":"Ogre","b":"1F479","j":["creature","face","fairy tale","fantasy","monster","troll","red","mask","halloween","scary","creepy","devil","demon","japanese"]},"goblin":{"a":"Goblin","b":"1F47A","j":["creature","face","fairy tale","fantasy","monster","red","evil","mask","scary","creepy","japanese"]},"ghost":{"a":"Ghost","b":"1F47B","j":["creature","face","fairy tale","fantasy","monster","halloween","spooky","scary"]},"alien":{"a":"Alien","b":"1F47D","j":["creature","extraterrestrial","face","fantasy","ufo","UFO","paul","weird","outer_space"]},"alien-monster":{"a":"Alien Monster","b":"1F47E","j":["alien","creature","extraterrestrial","face","monster","ufo","game","arcade","play"]},"robot":{"a":"Robot","b":"1F916","j":["face","monster","computer","machine","bot"]},"grinning-cat":{"a":"Grinning Cat","b":"1F63A","j":["cat","face","grinning","mouth","open","smile","animal","cats","happy"]},"grinning-cat-with-smiling-eyes":{"a":"Grinning Cat with Smiling Eyes","b":"1F638","j":["cat","eye","face","grin","smile","animal","cats"]},"cat-with-tears-of-joy":{"a":"Cat with Tears of Joy","b":"1F639","j":["cat","face","joy","tear","animal","cats","haha","happy","tears"]},"smiling-cat-with-hearteyes":{"a":"Smiling Cat with Heart-Eyes","b":"1F63B","j":["cat","eye","face","heart","love","smile","smiling cat with heart-eyes","smiling_cat_with_heart_eyes","animal","like","affection","cats","valentines"]},"cat-with-wry-smile":{"a":"Cat with Wry Smile","b":"1F63C","j":["cat","face","ironic","smile","wry","animal","cats","smirk"]},"kissing-cat":{"a":"Kissing Cat","b":"1F63D","j":["cat","eye","face","kiss","animal","cats"]},"weary-cat":{"a":"Weary Cat","b":"1F640","j":["cat","face","oh","surprised","weary","animal","cats","munch","scared","scream"]},"crying-cat":{"a":"Crying Cat","b":"1F63F","j":["cat","cry","face","sad","tear","animal","tears","weep","cats","upset"]},"pouting-cat":{"a":"Pouting Cat","b":"1F63E","j":["cat","face","pouting","animal","cats"]},"seenoevil-monkey":{"a":"See-No-Evil Monkey","b":"1F648","j":["evil","face","forbidden","monkey","see","see-no-evil monkey","see_no_evil_monkey","animal","nature","haha"]},"hearnoevil-monkey":{"a":"Hear-No-Evil Monkey","b":"1F649","j":["evil","face","forbidden","hear","hear-no-evil monkey","monkey","hear_no_evil_monkey","animal","nature"]},"speaknoevil-monkey":{"a":"Speak-No-Evil Monkey","b":"1F64A","j":["evil","face","forbidden","monkey","speak","speak-no-evil monkey","speak_no_evil_monkey","animal","nature","omg"]},"love-letter":{"a":"Love Letter","b":"1F48C","j":["heart","letter","love","mail","email","like","affection","envelope","valentines"]},"heart-with-arrow":{"a":"Heart with Arrow","b":"1F498","j":["arrow","cupid","love","like","heart","affection","valentines"]},"heart-with-ribbon":{"a":"Heart with Ribbon","b":"1F49D","j":["ribbon","valentine","love","valentines"]},"sparkling-heart":{"a":"Sparkling Heart","b":"1F496","j":["excited","sparkle","love","like","affection","valentines"]},"growing-heart":{"a":"Growing Heart","b":"1F497","j":["excited","growing","nervous","pulse","like","love","affection","valentines","pink"]},"beating-heart":{"a":"Beating Heart","b":"1F493","j":["beating","heartbeat","pulsating","love","like","affection","valentines","pink","heart"]},"revolving-hearts":{"a":"Revolving Hearts","b":"1F49E","j":["revolving","love","like","affection","valentines"]},"two-hearts":{"a":"Two Hearts","b":"1F495","j":["love","like","affection","valentines","heart"]},"heart-decoration":{"a":"Heart Decoration","b":"1F49F","j":["heart","purple-square","love","like"]},"heart-exclamation":{"a":"Heart Exclamation","b":"2763","j":["exclamation","mark","punctuation","decoration","love"]},"broken-heart":{"a":"Broken Heart","b":"1F494","j":["break","broken","sad","sorry","heart","heartbreak"]},"heart-on-fire":{"a":"Heart on Fire","b":"2764-FE0F-200D-1F525","j":["burn","heart","love","lust","sacred heart","passionate","enthusiastic"]},"mending-heart":{"a":"Mending Heart","b":"2764-FE0F-200D-1FA79","j":["healthier","improving","mending","recovering","recuperating","well","broken heart","bandage","wounded"]},"red-heart":{"a":"Red Heart","b":"2764","j":["heart","love","like","valentines"]},"pink-heart":{"a":"⊛ Pink Heart","b":"1FA77","j":["cute","heart","like","love","pink"]},"orange-heart":{"a":"Orange Heart","b":"1F9E1","j":["orange","love","like","affection","valentines"]},"yellow-heart":{"a":"Yellow Heart","b":"1F49B","j":["yellow","love","like","affection","valentines"]},"green-heart":{"a":"Green Heart","b":"1F49A","j":["green","love","like","affection","valentines"]},"blue-heart":{"a":"Blue Heart","b":"1F499","j":["blue","love","like","affection","valentines"]},"light-blue-heart":{"a":"⊛ Light Blue Heart","b":"1FA75","j":["cyan","heart","light blue","light blue heart","teal"]},"purple-heart":{"a":"Purple Heart","b":"1F49C","j":["purple","love","like","affection","valentines"]},"brown-heart":{"a":"Brown Heart","b":"1F90E","j":["brown","heart","coffee"]},"black-heart":{"a":"Black Heart","b":"1F5A4","j":["black","evil","wicked"]},"grey-heart":{"a":"⊛ Grey Heart","b":"1FA76","j":["gray","grey heart","heart","silver","slate"]},"white-heart":{"a":"White Heart","b":"1F90D","j":["heart","white","pure"]},"kiss-mark":{"a":"Kiss Mark","b":"1F48B","j":["kiss","lips","face","love","like","affection","valentines"]},"hundred-points":{"a":"Hundred Points","b":"1F4AF","j":["100","full","hundred","score","perfect","numbers","century","exam","quiz","test","pass"]},"anger-symbol":{"a":"Anger Symbol","b":"1F4A2","j":["angry","comic","mad"]},"collision":{"a":"Collision","b":"1F4A5","j":["boom","comic","bomb","explode","explosion","blown"]},"dizzy":{"a":"Dizzy","b":"1F4AB","j":["comic","star","sparkle","shoot","magic"]},"sweat-droplets":{"a":"Sweat Droplets","b":"1F4A6","j":["comic","splashing","sweat","water","drip","oops"]},"dashing-away":{"a":"Dashing Away","b":"1F4A8","j":["comic","dash","running","wind","air","fast","shoo","fart","smoke","puff"]},"hole":{"a":"Hole","b":"1F573","j":["embarrassing"]},"speech-balloon":{"a":"Speech Balloon","b":"1F4AC","j":["balloon","bubble","comic","dialog","speech","words","message","talk","chatting"]},"eye-in-speech-bubble":{"a":"Eye in Speech Bubble","b":"1F441-FE0F-200D-1F5E8-FE0F","j":["balloon","bubble","eye","speech","witness","info"]},"left-speech-bubble":{"a":"Left Speech Bubble","b":"1F5E8","j":["balloon","bubble","dialog","speech","words","message","talk","chatting"]},"right-anger-bubble":{"a":"Right Anger Bubble","b":"1F5EF","j":["angry","balloon","bubble","mad","caption","speech","thinking"]},"thought-balloon":{"a":"Thought Balloon","b":"1F4AD","j":["balloon","bubble","comic","thought","cloud","speech","thinking","dream"]},"zzz":{"a":"Zzz","b":"1F4A4","j":["comic","good night","sleep","ZZZ","sleepy","tired","dream"]},"waving-hand":{"a":"Waving Hand","b":"1F44B","j":["hand","wave","waving","hands","gesture","goodbye","solong","farewell","hello","hi","palm"]},"raised-back-of-hand":{"a":"Raised Back of Hand","b":"1F91A","j":["backhand","raised","fingers"]},"hand-with-fingers-splayed":{"a":"Hand with Fingers Splayed","b":"1F590","j":["finger","hand","splayed","fingers","palm"]},"raised-hand":{"a":"Raised Hand","b":"270B","j":["hand","high 5","high five","fingers","stop","highfive","palm","ban"]},"vulcan-salute":{"a":"Vulcan Salute","b":"1F596","j":["finger","hand","spock","vulcan","fingers","star trek"]},"rightwards-hand":{"a":"Rightwards Hand","b":"1FAF1","j":["hand","right","rightward","palm","offer"]},"leftwards-hand":{"a":"Leftwards Hand","b":"1FAF2","j":["hand","left","leftward","palm","offer"]},"palm-down-hand":{"a":"Palm Down Hand","b":"1FAF3","j":["dismiss","drop","shoo","palm"]},"palm-up-hand":{"a":"Palm Up Hand","b":"1FAF4","j":["beckon","catch","come","offer","lift","demand"]},"leftwards-pushing-hand":{"a":"⊛ Leftwards Pushing Hand","b":"1FAF7","j":["high five","leftward","leftwards pushing hand","push","refuse","stop","wait"]},"rightwards-pushing-hand":{"a":"⊛ Rightwards Pushing Hand","b":"1FAF8","j":["high five","push","refuse","rightward","rightwards pushing hand","stop","wait"]},"ok-hand":{"a":"Ok Hand","b":"1F44C","j":["hand","OK","fingers","limbs","perfect","ok","okay"]},"pinched-fingers":{"a":"Pinched Fingers","b":"1F90C","j":["fingers","hand gesture","interrogation","pinched","sarcastic","size","tiny","small"]},"pinching-hand":{"a":"Pinching Hand","b":"1F90F","j":["small amount","tiny","small","size"]},"victory-hand":{"a":"Victory Hand","b":"270C","j":["hand","v","victory","fingers","ohyeah","peace","two"]},"crossed-fingers":{"a":"Crossed Fingers","b":"1F91E","j":["cross","finger","hand","luck","good","lucky"]},"hand-with-index-finger-and-thumb-crossed":{"a":"Hand with Index Finger and Thumb Crossed","b":"1FAF0","j":["expensive","heart","love","money","snap"]},"loveyou-gesture":{"a":"Love-You Gesture","b":"1F91F","j":["hand","ILY","love-you gesture","love_you_gesture","fingers","gesture"]},"sign-of-the-horns":{"a":"Sign of the Horns","b":"1F918","j":["finger","hand","horns","rock-on","fingers","evil_eye","sign_of_horns","rock_on"]},"call-me-hand":{"a":"Call Me Hand","b":"1F919","j":["call","hand","hang loose","Shaka","hands","gesture","shaka"]},"backhand-index-pointing-left":{"a":"Backhand Index Pointing Left","b":"1F448","j":["backhand","finger","hand","index","point","direction","fingers","left"]},"backhand-index-pointing-right":{"a":"Backhand Index Pointing Right","b":"1F449","j":["backhand","finger","hand","index","point","fingers","direction","right"]},"backhand-index-pointing-up":{"a":"Backhand Index Pointing Up","b":"1F446","j":["backhand","finger","hand","point","up","fingers","direction"]},"middle-finger":{"a":"Middle Finger","b":"1F595","j":["finger","hand","fingers","rude","middle","flipping"]},"backhand-index-pointing-down":{"a":"Backhand Index Pointing Down","b":"1F447","j":["backhand","down","finger","hand","point","fingers","direction"]},"index-pointing-up":{"a":"Index Pointing Up","b":"261D","j":["finger","hand","index","point","up","fingers","direction"]},"index-pointing-at-the-viewer":{"a":"Index Pointing at the Viewer","b":"1FAF5","j":["point","you","recruit"]},"thumbs-up":{"a":"Thumbs Up","b":"1F44D","j":["+1","hand","thumb","up","thumbsup","yes","awesome","good","agree","accept","cool","like"]},"thumbs-down":{"a":"Thumbs Down","b":"1F44E","j":["-1","down","hand","thumb","thumbsdown","no","dislike"]},"raised-fist":{"a":"Raised Fist","b":"270A","j":["clenched","fist","hand","punch","fingers","grasp"]},"oncoming-fist":{"a":"Oncoming Fist","b":"1F44A","j":["clenched","fist","hand","punch","angry","violence","hit","attack"]},"leftfacing-fist":{"a":"Left-Facing Fist","b":"1F91B","j":["fist","left-facing fist","leftwards","left_facing_fist","hand","fistbump"]},"rightfacing-fist":{"a":"Right-Facing Fist","b":"1F91C","j":["fist","right-facing fist","rightwards","right_facing_fist","hand","fistbump"]},"clapping-hands":{"a":"Clapping Hands","b":"1F44F","j":["clap","hand","hands","praise","applause","congrats","yay"]},"raising-hands":{"a":"Raising Hands","b":"1F64C","j":["celebration","gesture","hand","hooray","raised","yea","hands"]},"heart-hands":{"a":"Heart Hands","b":"1FAF6","j":["love","appreciation","support"]},"open-hands":{"a":"Open Hands","b":"1F450","j":["hand","open","fingers","butterfly","hands"]},"palms-up-together":{"a":"Palms Up Together","b":"1F932","j":["prayer","cupped hands","hands","gesture","cupped"]},"handshake":{"a":"Handshake","b":"1F91D","j":["agreement","hand","meeting","shake"]},"folded-hands":{"a":"Folded Hands","b":"1F64F","j":["ask","hand","high 5","high five","please","pray","thanks","hope","wish","namaste","highfive","thank you","appreciate"]},"writing-hand":{"a":"Writing Hand","b":"270D","j":["hand","write","lower_left_ballpoint_pen","stationery","compose"]},"nail-polish":{"a":"Nail Polish","b":"1F485","j":["care","cosmetics","manicure","nail","polish","beauty","finger","fashion"]},"selfie":{"a":"Selfie","b":"1F933","j":["camera","phone"]},"flexed-biceps":{"a":"Flexed Biceps","b":"1F4AA","j":["biceps","comic","flex","muscle","arm","hand","summer","strong"]},"mechanical-arm":{"a":"Mechanical Arm","b":"1F9BE","j":["accessibility","prosthetic"]},"mechanical-leg":{"a":"Mechanical Leg","b":"1F9BF","j":["accessibility","prosthetic"]},"leg":{"a":"Leg","b":"1F9B5","j":["kick","limb"]},"foot":{"a":"Foot","b":"1F9B6","j":["kick","stomp"]},"ear":{"a":"Ear","b":"1F442","j":["body","face","hear","sound","listen"]},"ear-with-hearing-aid":{"a":"Ear with Hearing Aid","b":"1F9BB","j":["accessibility","hard of hearing"]},"nose":{"a":"Nose","b":"1F443","j":["body","smell","sniff"]},"brain":{"a":"Brain","b":"1F9E0","j":["intelligent","smart"]},"anatomical-heart":{"a":"Anatomical Heart","b":"1FAC0","j":["anatomical","cardiology","heart","organ","pulse","health","heartbeat"]},"lungs":{"a":"Lungs","b":"1FAC1","j":["breath","exhalation","inhalation","organ","respiration","breathe"]},"tooth":{"a":"Tooth","b":"1F9B7","j":["dentist","teeth"]},"bone":{"a":"Bone","b":"1F9B4","j":["skeleton"]},"eyes":{"a":"Eyes","b":"1F440","j":["eye","face","look","watch","stalk","peek","see"]},"eye":{"a":"Eye","b":"1F441","j":["body","face","look","see","watch","stare"]},"tongue":{"a":"Tongue","b":"1F445","j":["body","mouth","playful"]},"mouth":{"a":"Mouth","b":"1F444","j":["lips","kiss"]},"biting-lip":{"a":"Biting Lip","b":"1FAE6","j":["anxious","fear","flirting","nervous","uncomfortable","worried","flirt","sexy","pain","worry"]},"baby":{"a":"Baby","b":"1F476","j":["young","child","boy","girl","toddler"]},"child":{"a":"Child","b":"1F9D2","j":["gender-neutral","unspecified gender","young"]},"boy":{"a":"Boy","b":"1F466","j":["young","man","male","guy","teenager"]},"girl":{"a":"Girl","b":"1F467","j":["Virgo","young","zodiac","female","woman","teenager"]},"person":{"a":"Person","b":"1F9D1","j":["adult","gender-neutral","unspecified gender"]},"person-blond-hair":{"a":"Person: Blond Hair","b":"1F471","j":["blond","blond-haired person","hair","person: blond hair","hairstyle"]},"man":{"a":"Man","b":"1F468","j":["adult","mustache","father","dad","guy","classy","sir","moustache"]},"person-beard":{"a":"Person: Beard","b":"1F9D4","j":["beard","person","person: beard","bewhiskered","man_beard"]},"man-beard":{"a":"Man: Beard","b":"1F9D4-200D-2642-FE0F","j":["beard","man","man: beard","facial hair"]},"woman-beard":{"a":"Woman: Beard","b":"1F9D4-200D-2640-FE0F","j":["beard","woman","woman: beard","facial hair"]},"man-red-hair":{"a":"Man: Red Hair","b":"1F468-200D-1F9B0","j":["adult","man","red hair","hairstyle"]},"man-curly-hair":{"a":"Man: Curly Hair","b":"1F468-200D-1F9B1","j":["adult","curly hair","man","hairstyle"]},"man-white-hair":{"a":"Man: White Hair","b":"1F468-200D-1F9B3","j":["adult","man","white hair","old","elder"]},"man-bald":{"a":"Man: Bald","b":"1F468-200D-1F9B2","j":["adult","bald","man","hairless"]},"woman":{"a":"Woman","b":"1F469","j":["adult","female","girls","lady"]},"woman-red-hair":{"a":"Woman: Red Hair","b":"1F469-200D-1F9B0","j":["adult","red hair","woman","hairstyle"]},"person-red-hair":{"a":"Person: Red Hair","b":"1F9D1-200D-1F9B0","j":["adult","gender-neutral","person","red hair","unspecified gender","hairstyle"]},"woman-curly-hair":{"a":"Woman: Curly Hair","b":"1F469-200D-1F9B1","j":["adult","curly hair","woman","hairstyle"]},"person-curly-hair":{"a":"Person: Curly Hair","b":"1F9D1-200D-1F9B1","j":["adult","curly hair","gender-neutral","person","unspecified gender","hairstyle"]},"woman-white-hair":{"a":"Woman: White Hair","b":"1F469-200D-1F9B3","j":["adult","white hair","woman","old","elder"]},"person-white-hair":{"a":"Person: White Hair","b":"1F9D1-200D-1F9B3","j":["adult","gender-neutral","person","unspecified gender","white hair","elder","old"]},"woman-bald":{"a":"Woman: Bald","b":"1F469-200D-1F9B2","j":["adult","bald","woman","hairless"]},"person-bald":{"a":"Person: Bald","b":"1F9D1-200D-1F9B2","j":["adult","bald","gender-neutral","person","unspecified gender","hairless"]},"woman-blond-hair":{"a":"Woman: Blond Hair","b":"1F471-200D-2640-FE0F","j":["blond-haired woman","blonde","hair","woman","woman: blond hair","female","girl","person"]},"man-blond-hair":{"a":"Man: Blond Hair","b":"1F471-200D-2642-FE0F","j":["blond","blond-haired man","hair","man","man: blond hair","male","boy","blonde","guy","person"]},"older-person":{"a":"Older Person","b":"1F9D3","j":["adult","gender-neutral","old","unspecified gender","human","elder","senior"]},"old-man":{"a":"Old Man","b":"1F474","j":["adult","man","old","human","male","men","elder","senior"]},"old-woman":{"a":"Old Woman","b":"1F475","j":["adult","old","woman","human","female","women","lady","elder","senior"]},"person-frowning":{"a":"Person Frowning","b":"1F64D","j":["frown","gesture","worried"]},"man-frowning":{"a":"Man Frowning","b":"1F64D-200D-2642-FE0F","j":["frowning","gesture","man","male","boy","sad","depressed","discouraged","unhappy"]},"woman-frowning":{"a":"Woman Frowning","b":"1F64D-200D-2640-FE0F","j":["frowning","gesture","woman","female","girl","sad","depressed","discouraged","unhappy"]},"person-pouting":{"a":"Person Pouting","b":"1F64E","j":["gesture","pouting","upset"]},"man-pouting":{"a":"Man Pouting","b":"1F64E-200D-2642-FE0F","j":["gesture","man","pouting","male","boy"]},"woman-pouting":{"a":"Woman Pouting","b":"1F64E-200D-2640-FE0F","j":["gesture","pouting","woman","female","girl"]},"person-gesturing-no":{"a":"Person Gesturing No","b":"1F645","j":["forbidden","gesture","hand","person gesturing NO","prohibited","decline"]},"man-gesturing-no":{"a":"Man Gesturing No","b":"1F645-200D-2642-FE0F","j":["forbidden","gesture","hand","man","man gesturing NO","prohibited","male","boy","nope"]},"woman-gesturing-no":{"a":"Woman Gesturing No","b":"1F645-200D-2640-FE0F","j":["forbidden","gesture","hand","prohibited","woman","woman gesturing NO","female","girl","nope"]},"person-gesturing-ok":{"a":"Person Gesturing Ok","b":"1F646","j":["gesture","hand","OK","person gesturing OK","agree"]},"man-gesturing-ok":{"a":"Man Gesturing Ok","b":"1F646-200D-2642-FE0F","j":["gesture","hand","man","man gesturing OK","OK","men","boy","male","blue","human"]},"woman-gesturing-ok":{"a":"Woman Gesturing Ok","b":"1F646-200D-2640-FE0F","j":["gesture","hand","OK","woman","woman gesturing OK","women","girl","female","pink","human"]},"person-tipping-hand":{"a":"Person Tipping Hand","b":"1F481","j":["hand","help","information","sassy","tipping"]},"man-tipping-hand":{"a":"Man Tipping Hand","b":"1F481-200D-2642-FE0F","j":["man","sassy","tipping hand","male","boy","human","information"]},"woman-tipping-hand":{"a":"Woman Tipping Hand","b":"1F481-200D-2640-FE0F","j":["sassy","tipping hand","woman","female","girl","human","information"]},"person-raising-hand":{"a":"Person Raising Hand","b":"1F64B","j":["gesture","hand","happy","raised","question"]},"man-raising-hand":{"a":"Man Raising Hand","b":"1F64B-200D-2642-FE0F","j":["gesture","man","raising hand","male","boy"]},"woman-raising-hand":{"a":"Woman Raising Hand","b":"1F64B-200D-2640-FE0F","j":["gesture","raising hand","woman","female","girl"]},"deaf-person":{"a":"Deaf Person","b":"1F9CF","j":["accessibility","deaf","ear","hear"]},"deaf-man":{"a":"Deaf Man","b":"1F9CF-200D-2642-FE0F","j":["deaf","man","accessibility"]},"deaf-woman":{"a":"Deaf Woman","b":"1F9CF-200D-2640-FE0F","j":["deaf","woman","accessibility"]},"person-bowing":{"a":"Person Bowing","b":"1F647","j":["apology","bow","gesture","sorry","respectiful"]},"man-bowing":{"a":"Man Bowing","b":"1F647-200D-2642-FE0F","j":["apology","bowing","favor","gesture","man","sorry","male","boy"]},"woman-bowing":{"a":"Woman Bowing","b":"1F647-200D-2640-FE0F","j":["apology","bowing","favor","gesture","sorry","woman","female","girl"]},"person-facepalming":{"a":"Person Facepalming","b":"1F926","j":["disbelief","exasperation","face","palm","disappointed"]},"man-facepalming":{"a":"Man Facepalming","b":"1F926-200D-2642-FE0F","j":["disbelief","exasperation","facepalm","man","male","boy"]},"woman-facepalming":{"a":"Woman Facepalming","b":"1F926-200D-2640-FE0F","j":["disbelief","exasperation","facepalm","woman","female","girl"]},"person-shrugging":{"a":"Person Shrugging","b":"1F937","j":["doubt","ignorance","indifference","shrug","regardless"]},"man-shrugging":{"a":"Man Shrugging","b":"1F937-200D-2642-FE0F","j":["doubt","ignorance","indifference","man","shrug","male","boy","confused","indifferent"]},"woman-shrugging":{"a":"Woman Shrugging","b":"1F937-200D-2640-FE0F","j":["doubt","ignorance","indifference","shrug","woman","female","girl","confused","indifferent"]},"health-worker":{"a":"Health Worker","b":"1F9D1-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","hospital"]},"man-health-worker":{"a":"Man Health Worker","b":"1F468-200D-2695-FE0F","j":["doctor","healthcare","man","nurse","therapist","human"]},"woman-health-worker":{"a":"Woman Health Worker","b":"1F469-200D-2695-FE0F","j":["doctor","healthcare","nurse","therapist","woman","human"]},"student":{"a":"Student","b":"1F9D1-200D-1F393","j":["graduate","learn"]},"man-student":{"a":"Man Student","b":"1F468-200D-1F393","j":["graduate","man","student","human"]},"woman-student":{"a":"Woman Student","b":"1F469-200D-1F393","j":["graduate","student","woman","human"]},"teacher":{"a":"Teacher","b":"1F9D1-200D-1F3EB","j":["instructor","professor"]},"man-teacher":{"a":"Man Teacher","b":"1F468-200D-1F3EB","j":["instructor","man","professor","teacher","human"]},"woman-teacher":{"a":"Woman Teacher","b":"1F469-200D-1F3EB","j":["instructor","professor","teacher","woman","human"]},"judge":{"a":"Judge","b":"1F9D1-200D-2696-FE0F","j":["justice","scales","law"]},"man-judge":{"a":"Man Judge","b":"1F468-200D-2696-FE0F","j":["judge","justice","man","scales","court","human"]},"woman-judge":{"a":"Woman Judge","b":"1F469-200D-2696-FE0F","j":["judge","justice","scales","woman","court","human"]},"farmer":{"a":"Farmer","b":"1F9D1-200D-1F33E","j":["gardener","rancher","crops"]},"man-farmer":{"a":"Man Farmer","b":"1F468-200D-1F33E","j":["farmer","gardener","man","rancher","human"]},"woman-farmer":{"a":"Woman Farmer","b":"1F469-200D-1F33E","j":["farmer","gardener","rancher","woman","human"]},"cook":{"a":"Cook","b":"1F9D1-200D-1F373","j":["chef","food","kitchen","culinary"]},"man-cook":{"a":"Man Cook","b":"1F468-200D-1F373","j":["chef","cook","man","human"]},"woman-cook":{"a":"Woman Cook","b":"1F469-200D-1F373","j":["chef","cook","woman","human"]},"mechanic":{"a":"Mechanic","b":"1F9D1-200D-1F527","j":["electrician","plumber","tradesperson","worker","technician"]},"man-mechanic":{"a":"Man Mechanic","b":"1F468-200D-1F527","j":["electrician","man","mechanic","plumber","tradesperson","human","wrench"]},"woman-mechanic":{"a":"Woman Mechanic","b":"1F469-200D-1F527","j":["electrician","mechanic","plumber","tradesperson","woman","human","wrench"]},"factory-worker":{"a":"Factory Worker","b":"1F9D1-200D-1F3ED","j":["assembly","factory","industrial","worker","labor"]},"man-factory-worker":{"a":"Man Factory Worker","b":"1F468-200D-1F3ED","j":["assembly","factory","industrial","man","worker","human"]},"woman-factory-worker":{"a":"Woman Factory Worker","b":"1F469-200D-1F3ED","j":["assembly","factory","industrial","woman","worker","human"]},"office-worker":{"a":"Office Worker","b":"1F9D1-200D-1F4BC","j":["architect","business","manager","white-collar"]},"man-office-worker":{"a":"Man Office Worker","b":"1F468-200D-1F4BC","j":["architect","business","man","manager","white-collar","human"]},"woman-office-worker":{"a":"Woman Office Worker","b":"1F469-200D-1F4BC","j":["architect","business","manager","white-collar","woman","human"]},"scientist":{"a":"Scientist","b":"1F9D1-200D-1F52C","j":["biologist","chemist","engineer","physicist","chemistry"]},"man-scientist":{"a":"Man Scientist","b":"1F468-200D-1F52C","j":["biologist","chemist","engineer","man","physicist","scientist","human"]},"woman-scientist":{"a":"Woman Scientist","b":"1F469-200D-1F52C","j":["biologist","chemist","engineer","physicist","scientist","woman","human"]},"technologist":{"a":"Technologist","b":"1F9D1-200D-1F4BB","j":["coder","developer","inventor","software","computer"]},"man-technologist":{"a":"Man Technologist","b":"1F468-200D-1F4BB","j":["coder","developer","inventor","man","software","technologist","engineer","programmer","human","laptop","computer"]},"woman-technologist":{"a":"Woman Technologist","b":"1F469-200D-1F4BB","j":["coder","developer","inventor","software","technologist","woman","engineer","programmer","human","laptop","computer"]},"singer":{"a":"Singer","b":"1F9D1-200D-1F3A4","j":["actor","entertainer","rock","star","song","artist","performer"]},"man-singer":{"a":"Man Singer","b":"1F468-200D-1F3A4","j":["actor","entertainer","man","rock","singer","star","rockstar","human"]},"woman-singer":{"a":"Woman Singer","b":"1F469-200D-1F3A4","j":["actor","entertainer","rock","singer","star","woman","rockstar","human"]},"artist":{"a":"Artist","b":"1F9D1-200D-1F3A8","j":["palette","painting","draw","creativity"]},"man-artist":{"a":"Man Artist","b":"1F468-200D-1F3A8","j":["artist","man","palette","painter","human"]},"woman-artist":{"a":"Woman Artist","b":"1F469-200D-1F3A8","j":["artist","palette","woman","painter","human"]},"pilot":{"a":"Pilot","b":"1F9D1-200D-2708-FE0F","j":["plane","fly","airplane"]},"man-pilot":{"a":"Man Pilot","b":"1F468-200D-2708-FE0F","j":["man","pilot","plane","aviator","human"]},"woman-pilot":{"a":"Woman Pilot","b":"1F469-200D-2708-FE0F","j":["pilot","plane","woman","aviator","human"]},"astronaut":{"a":"Astronaut","b":"1F9D1-200D-1F680","j":["rocket","outerspace"]},"man-astronaut":{"a":"Man Astronaut","b":"1F468-200D-1F680","j":["astronaut","man","rocket","space","human"]},"woman-astronaut":{"a":"Woman Astronaut","b":"1F469-200D-1F680","j":["astronaut","rocket","woman","space","human"]},"firefighter":{"a":"Firefighter","b":"1F9D1-200D-1F692","j":["firetruck","fire"]},"man-firefighter":{"a":"Man Firefighter","b":"1F468-200D-1F692","j":["firefighter","firetruck","man","fireman","human"]},"woman-firefighter":{"a":"Woman Firefighter","b":"1F469-200D-1F692","j":["firefighter","firetruck","woman","fireman","human"]},"police-officer":{"a":"Police Officer","b":"1F46E","j":["cop","officer","police"]},"man-police-officer":{"a":"Man Police Officer","b":"1F46E-200D-2642-FE0F","j":["cop","man","officer","police","law","legal","enforcement","arrest","911"]},"woman-police-officer":{"a":"Woman Police Officer","b":"1F46E-200D-2640-FE0F","j":["cop","officer","police","woman","law","legal","enforcement","arrest","911","female"]},"detective":{"a":"Detective","b":"1F575","j":["sleuth","spy","human"]},"man-detective":{"a":"Man Detective","b":"1F575-FE0F-200D-2642-FE0F","j":["detective","man","sleuth","spy","crime"]},"woman-detective":{"a":"Woman Detective","b":"1F575-FE0F-200D-2640-FE0F","j":["detective","sleuth","spy","woman","human","female"]},"guard":{"a":"Guard","b":"1F482","j":["protect"]},"man-guard":{"a":"Man Guard","b":"1F482-200D-2642-FE0F","j":["guard","man","uk","gb","british","male","guy","royal"]},"woman-guard":{"a":"Woman Guard","b":"1F482-200D-2640-FE0F","j":["guard","woman","uk","gb","british","female","royal"]},"ninja":{"a":"Ninja","b":"1F977","j":["fighter","hidden","stealth","ninjutsu","skills","japanese"]},"construction-worker":{"a":"Construction Worker","b":"1F477","j":["construction","hat","worker","labor","build"]},"man-construction-worker":{"a":"Man Construction Worker","b":"1F477-200D-2642-FE0F","j":["construction","man","worker","male","human","wip","guy","build","labor"]},"woman-construction-worker":{"a":"Woman Construction Worker","b":"1F477-200D-2640-FE0F","j":["construction","woman","worker","female","human","wip","build","labor"]},"person-with-crown":{"a":"Person with Crown","b":"1FAC5","j":["monarch","noble","regal","royalty","power"]},"prince":{"a":"Prince","b":"1F934","j":["boy","man","male","crown","royal","king"]},"princess":{"a":"Princess","b":"1F478","j":["fairy tale","fantasy","girl","woman","female","blond","crown","royal","queen"]},"person-wearing-turban":{"a":"Person Wearing Turban","b":"1F473","j":["turban","headdress"]},"man-wearing-turban":{"a":"Man Wearing Turban","b":"1F473-200D-2642-FE0F","j":["man","turban","male","indian","hinduism","arabs"]},"woman-wearing-turban":{"a":"Woman Wearing Turban","b":"1F473-200D-2640-FE0F","j":["turban","woman","female","indian","hinduism","arabs"]},"person-with-skullcap":{"a":"Person with Skullcap","b":"1F472","j":["cap","gua pi mao","hat","person","skullcap","man_with_skullcap","male","boy","chinese"]},"woman-with-headscarf":{"a":"Woman with Headscarf","b":"1F9D5","j":["headscarf","hijab","mantilla","tichel","bandana","head kerchief","female"]},"person-in-tuxedo":{"a":"Person in Tuxedo","b":"1F935","j":["groom","person","tuxedo","man_in_tuxedo","couple","marriage","wedding"]},"man-in-tuxedo":{"a":"Man in Tuxedo","b":"1F935-200D-2642-FE0F","j":["man","tuxedo","formal","fashion"]},"woman-in-tuxedo":{"a":"Woman in Tuxedo","b":"1F935-200D-2640-FE0F","j":["tuxedo","woman","formal","fashion"]},"person-with-veil":{"a":"Person with Veil","b":"1F470","j":["bride","person","veil","wedding","bride_with_veil","couple","marriage","woman"]},"man-with-veil":{"a":"Man with Veil","b":"1F470-200D-2642-FE0F","j":["man","veil","wedding","marriage"]},"woman-with-veil":{"a":"Woman with Veil","b":"1F470-200D-2640-FE0F","j":["veil","woman","wedding","marriage"]},"pregnant-woman":{"a":"Pregnant Woman","b":"1F930","j":["pregnant","woman","baby"]},"pregnant-man":{"a":"Pregnant Man","b":"1FAC3","j":["belly","bloated","full","pregnant","baby"]},"pregnant-person":{"a":"Pregnant Person","b":"1FAC4","j":["belly","bloated","full","pregnant","baby"]},"breastfeeding":{"a":"Breast-Feeding","b":"1F931","j":["baby","breast","breast-feeding","nursing","breast_feeding"]},"woman-feeding-baby":{"a":"Woman Feeding Baby","b":"1F469-200D-1F37C","j":["baby","feeding","nursing","woman","birth","food"]},"man-feeding-baby":{"a":"Man Feeding Baby","b":"1F468-200D-1F37C","j":["baby","feeding","man","nursing","birth","food"]},"person-feeding-baby":{"a":"Person Feeding Baby","b":"1F9D1-200D-1F37C","j":["baby","feeding","nursing","person","birth","food"]},"baby-angel":{"a":"Baby Angel","b":"1F47C","j":["angel","baby","face","fairy tale","fantasy","heaven","wings","halo"]},"santa-claus":{"a":"Santa Claus","b":"1F385","j":["celebration","Christmas","claus","father","santa","festival","man","male","xmas","father christmas"]},"mrs-claus":{"a":"Mrs. Claus","b":"1F936","j":["celebration","Christmas","claus","mother","Mrs.","woman","female","xmas","mother christmas"]},"mx-claus":{"a":"Mx Claus","b":"1F9D1-200D-1F384","j":["Claus, christmas","christmas"]},"superhero":{"a":"Superhero","b":"1F9B8","j":["good","hero","heroine","superpower","marvel"]},"man-superhero":{"a":"Man Superhero","b":"1F9B8-200D-2642-FE0F","j":["good","hero","man","superpower","male","superpowers"]},"woman-superhero":{"a":"Woman Superhero","b":"1F9B8-200D-2640-FE0F","j":["good","hero","heroine","superpower","woman","female","superpowers"]},"supervillain":{"a":"Supervillain","b":"1F9B9","j":["criminal","evil","superpower","villain","marvel"]},"man-supervillain":{"a":"Man Supervillain","b":"1F9B9-200D-2642-FE0F","j":["criminal","evil","man","superpower","villain","male","bad","hero","superpowers"]},"woman-supervillain":{"a":"Woman Supervillain","b":"1F9B9-200D-2640-FE0F","j":["criminal","evil","superpower","villain","woman","female","bad","heroine","superpowers"]},"mage":{"a":"Mage","b":"1F9D9","j":["sorcerer","sorceress","witch","wizard","magic"]},"man-mage":{"a":"Man Mage","b":"1F9D9-200D-2642-FE0F","j":["sorcerer","wizard","man","male","mage"]},"woman-mage":{"a":"Woman Mage","b":"1F9D9-200D-2640-FE0F","j":["sorceress","witch","woman","female","mage"]},"fairy":{"a":"Fairy","b":"1F9DA","j":["Oberon","Puck","Titania","wings","magical"]},"man-fairy":{"a":"Man Fairy","b":"1F9DA-200D-2642-FE0F","j":["Oberon","Puck","man","male"]},"woman-fairy":{"a":"Woman Fairy","b":"1F9DA-200D-2640-FE0F","j":["Titania","woman","female"]},"vampire":{"a":"Vampire","b":"1F9DB","j":["Dracula","undead","blood","twilight"]},"man-vampire":{"a":"Man Vampire","b":"1F9DB-200D-2642-FE0F","j":["Dracula","undead","man","male","dracula"]},"woman-vampire":{"a":"Woman Vampire","b":"1F9DB-200D-2640-FE0F","j":["undead","woman","female"]},"merperson":{"a":"Merperson","b":"1F9DC","j":["mermaid","merman","merwoman","sea"]},"merman":{"a":"Merman","b":"1F9DC-200D-2642-FE0F","j":["Triton","man","male","triton"]},"mermaid":{"a":"Mermaid","b":"1F9DC-200D-2640-FE0F","j":["merwoman","woman","female","ariel"]},"elf":{"a":"Elf","b":"1F9DD","j":["magical","LOTR style"]},"man-elf":{"a":"Man Elf","b":"1F9DD-200D-2642-FE0F","j":["magical","man","male"]},"woman-elf":{"a":"Woman Elf","b":"1F9DD-200D-2640-FE0F","j":["magical","woman","female"]},"genie":{"a":"Genie","b":"1F9DE","j":["djinn","(non-human color)","magical","wishes"]},"man-genie":{"a":"Man Genie","b":"1F9DE-200D-2642-FE0F","j":["djinn","man","male"]},"woman-genie":{"a":"Woman Genie","b":"1F9DE-200D-2640-FE0F","j":["djinn","woman","female"]},"zombie":{"a":"Zombie","b":"1F9DF","j":["undead","walking dead","(non-human color)","dead"]},"man-zombie":{"a":"Man Zombie","b":"1F9DF-200D-2642-FE0F","j":["undead","walking dead","man","male","dracula"]},"woman-zombie":{"a":"Woman Zombie","b":"1F9DF-200D-2640-FE0F","j":["undead","walking dead","woman","female"]},"troll":{"a":"Troll","b":"1F9CC","j":["fairy tale","fantasy","monster","mystical"]},"person-getting-massage":{"a":"Person Getting Massage","b":"1F486","j":["face","massage","salon","relax"]},"man-getting-massage":{"a":"Man Getting Massage","b":"1F486-200D-2642-FE0F","j":["face","man","massage","male","boy","head"]},"woman-getting-massage":{"a":"Woman Getting Massage","b":"1F486-200D-2640-FE0F","j":["face","massage","woman","female","girl","head"]},"person-getting-haircut":{"a":"Person Getting Haircut","b":"1F487","j":["barber","beauty","haircut","parlor","hairstyle"]},"man-getting-haircut":{"a":"Man Getting Haircut","b":"1F487-200D-2642-FE0F","j":["haircut","man","male","boy"]},"woman-getting-haircut":{"a":"Woman Getting Haircut","b":"1F487-200D-2640-FE0F","j":["haircut","woman","female","girl"]},"person-walking":{"a":"Person Walking","b":"1F6B6","j":["hike","walk","walking","move"]},"man-walking":{"a":"Man Walking","b":"1F6B6-200D-2642-FE0F","j":["hike","man","walk","human","feet","steps"]},"woman-walking":{"a":"Woman Walking","b":"1F6B6-200D-2640-FE0F","j":["hike","walk","woman","human","feet","steps","female"]},"person-standing":{"a":"Person Standing","b":"1F9CD","j":["stand","standing","still"]},"man-standing":{"a":"Man Standing","b":"1F9CD-200D-2642-FE0F","j":["man","standing","still"]},"woman-standing":{"a":"Woman Standing","b":"1F9CD-200D-2640-FE0F","j":["standing","woman","still"]},"person-kneeling":{"a":"Person Kneeling","b":"1F9CE","j":["kneel","kneeling","pray","respectful"]},"man-kneeling":{"a":"Man Kneeling","b":"1F9CE-200D-2642-FE0F","j":["kneeling","man","pray","respectful"]},"woman-kneeling":{"a":"Woman Kneeling","b":"1F9CE-200D-2640-FE0F","j":["kneeling","woman","respectful","pray"]},"person-with-white-cane":{"a":"Person with White Cane","b":"1F9D1-200D-1F9AF","j":["accessibility","blind","person_with_probing_cane"]},"man-with-white-cane":{"a":"Man with White Cane","b":"1F468-200D-1F9AF","j":["accessibility","blind","man","man_with_probing_cane"]},"woman-with-white-cane":{"a":"Woman with White Cane","b":"1F469-200D-1F9AF","j":["accessibility","blind","woman","woman_with_probing_cane"]},"person-in-motorized-wheelchair":{"a":"Person in Motorized Wheelchair","b":"1F9D1-200D-1F9BC","j":["accessibility","wheelchair","disability"]},"man-in-motorized-wheelchair":{"a":"Man in Motorized Wheelchair","b":"1F468-200D-1F9BC","j":["accessibility","man","wheelchair","disability"]},"woman-in-motorized-wheelchair":{"a":"Woman in Motorized Wheelchair","b":"1F469-200D-1F9BC","j":["accessibility","wheelchair","woman","disability"]},"person-in-manual-wheelchair":{"a":"Person in Manual Wheelchair","b":"1F9D1-200D-1F9BD","j":["accessibility","wheelchair","disability"]},"man-in-manual-wheelchair":{"a":"Man in Manual Wheelchair","b":"1F468-200D-1F9BD","j":["accessibility","man","wheelchair","disability"]},"woman-in-manual-wheelchair":{"a":"Woman in Manual Wheelchair","b":"1F469-200D-1F9BD","j":["accessibility","wheelchair","woman","disability"]},"person-running":{"a":"Person Running","b":"1F3C3","j":["marathon","running","move"]},"man-running":{"a":"Man Running","b":"1F3C3-200D-2642-FE0F","j":["man","marathon","racing","running","walking","exercise","race"]},"woman-running":{"a":"Woman Running","b":"1F3C3-200D-2640-FE0F","j":["marathon","racing","running","woman","walking","exercise","race","female"]},"woman-dancing":{"a":"Woman Dancing","b":"1F483","j":["dance","dancing","woman","female","girl","fun"]},"man-dancing":{"a":"Man Dancing","b":"1F57A","j":["dance","dancing","man","male","boy","fun","dancer"]},"person-in-suit-levitating":{"a":"Person in Suit Levitating","b":"1F574","j":["business","person","suit","man_in_suit_levitating","levitate","hover","jump"]},"people-with-bunny-ears":{"a":"People with Bunny Ears","b":"1F46F","j":["bunny ear","dancer","partying","perform","costume"]},"men-with-bunny-ears":{"a":"Men with Bunny Ears","b":"1F46F-200D-2642-FE0F","j":["bunny ear","dancer","men","partying","male","bunny","boys"]},"women-with-bunny-ears":{"a":"Women with Bunny Ears","b":"1F46F-200D-2640-FE0F","j":["bunny ear","dancer","partying","women","female","bunny","girls"]},"person-in-steamy-room":{"a":"Person in Steamy Room","b":"1F9D6","j":["sauna","steam room","hamam","steambath","relax","spa"]},"man-in-steamy-room":{"a":"Man in Steamy Room","b":"1F9D6-200D-2642-FE0F","j":["sauna","steam room","male","man","spa","steamroom"]},"woman-in-steamy-room":{"a":"Woman in Steamy Room","b":"1F9D6-200D-2640-FE0F","j":["sauna","steam room","female","woman","spa","steamroom"]},"person-climbing":{"a":"Person Climbing","b":"1F9D7","j":["climber","sport"]},"man-climbing":{"a":"Man Climbing","b":"1F9D7-200D-2642-FE0F","j":["climber","sports","hobby","man","male","rock"]},"woman-climbing":{"a":"Woman Climbing","b":"1F9D7-200D-2640-FE0F","j":["climber","sports","hobby","woman","female","rock"]},"person-fencing":{"a":"Person Fencing","b":"1F93A","j":["fencer","fencing","sword","sports"]},"horse-racing":{"a":"Horse Racing","b":"1F3C7","j":["horse","jockey","racehorse","racing","animal","betting","competition","gambling","luck"]},"skier":{"a":"Skier","b":"26F7","j":["ski","snow","sports","winter"]},"snowboarder":{"a":"Snowboarder","b":"1F3C2","j":["ski","snow","snowboard","sports","winter"]},"person-golfing":{"a":"Person Golfing","b":"1F3CC","j":["ball","golf","sports","business"]},"man-golfing":{"a":"Man Golfing","b":"1F3CC-FE0F-200D-2642-FE0F","j":["golf","man","sport"]},"woman-golfing":{"a":"Woman Golfing","b":"1F3CC-FE0F-200D-2640-FE0F","j":["golf","woman","sports","business","female"]},"person-surfing":{"a":"Person Surfing","b":"1F3C4","j":["surfing","sport","sea"]},"man-surfing":{"a":"Man Surfing","b":"1F3C4-200D-2642-FE0F","j":["man","surfing","sports","ocean","sea","summer","beach"]},"woman-surfing":{"a":"Woman Surfing","b":"1F3C4-200D-2640-FE0F","j":["surfing","woman","sports","ocean","sea","summer","beach","female"]},"person-rowing-boat":{"a":"Person Rowing Boat","b":"1F6A3","j":["boat","rowboat","sport","move"]},"man-rowing-boat":{"a":"Man Rowing Boat","b":"1F6A3-200D-2642-FE0F","j":["boat","man","rowboat","sports","hobby","water","ship"]},"woman-rowing-boat":{"a":"Woman Rowing Boat","b":"1F6A3-200D-2640-FE0F","j":["boat","rowboat","woman","sports","hobby","water","ship","female"]},"person-swimming":{"a":"Person Swimming","b":"1F3CA","j":["swim","sport","pool"]},"man-swimming":{"a":"Man Swimming","b":"1F3CA-200D-2642-FE0F","j":["man","swim","sports","exercise","human","athlete","water","summer"]},"woman-swimming":{"a":"Woman Swimming","b":"1F3CA-200D-2640-FE0F","j":["swim","woman","sports","exercise","human","athlete","water","summer","female"]},"person-bouncing-ball":{"a":"Person Bouncing Ball","b":"26F9","j":["ball","sports","human"]},"man-bouncing-ball":{"a":"Man Bouncing Ball","b":"26F9-FE0F-200D-2642-FE0F","j":["ball","man","sport"]},"woman-bouncing-ball":{"a":"Woman Bouncing Ball","b":"26F9-FE0F-200D-2640-FE0F","j":["ball","woman","sports","human","female"]},"person-lifting-weights":{"a":"Person Lifting Weights","b":"1F3CB","j":["lifter","weight","sports","training","exercise"]},"man-lifting-weights":{"a":"Man Lifting Weights","b":"1F3CB-FE0F-200D-2642-FE0F","j":["man","weight lifter","sport"]},"woman-lifting-weights":{"a":"Woman Lifting Weights","b":"1F3CB-FE0F-200D-2640-FE0F","j":["weight lifter","woman","sports","training","exercise","female"]},"person-biking":{"a":"Person Biking","b":"1F6B4","j":["bicycle","biking","cyclist","sport","move"]},"man-biking":{"a":"Man Biking","b":"1F6B4-200D-2642-FE0F","j":["bicycle","biking","cyclist","man","sports","bike","exercise","hipster"]},"woman-biking":{"a":"Woman Biking","b":"1F6B4-200D-2640-FE0F","j":["bicycle","biking","cyclist","woman","sports","bike","exercise","hipster","female"]},"person-mountain-biking":{"a":"Person Mountain Biking","b":"1F6B5","j":["bicycle","bicyclist","bike","cyclist","mountain","sport","move"]},"man-mountain-biking":{"a":"Man Mountain Biking","b":"1F6B5-200D-2642-FE0F","j":["bicycle","bike","cyclist","man","mountain","transportation","sports","human","race"]},"woman-mountain-biking":{"a":"Woman Mountain Biking","b":"1F6B5-200D-2640-FE0F","j":["bicycle","bike","biking","cyclist","mountain","woman","transportation","sports","human","race","female"]},"person-cartwheeling":{"a":"Person Cartwheeling","b":"1F938","j":["cartwheel","gymnastics","sport","gymnastic"]},"man-cartwheeling":{"a":"Man Cartwheeling","b":"1F938-200D-2642-FE0F","j":["cartwheel","gymnastics","man"]},"woman-cartwheeling":{"a":"Woman Cartwheeling","b":"1F938-200D-2640-FE0F","j":["cartwheel","gymnastics","woman"]},"people-wrestling":{"a":"People Wrestling","b":"1F93C","j":["wrestle","wrestler","sport"]},"men-wrestling":{"a":"Men Wrestling","b":"1F93C-200D-2642-FE0F","j":["men","wrestle","sports","wrestlers"]},"women-wrestling":{"a":"Women Wrestling","b":"1F93C-200D-2640-FE0F","j":["women","wrestle","sports","wrestlers"]},"person-playing-water-polo":{"a":"Person Playing Water Polo","b":"1F93D","j":["polo","water","sport"]},"man-playing-water-polo":{"a":"Man Playing Water Polo","b":"1F93D-200D-2642-FE0F","j":["man","water polo","sports","pool"]},"woman-playing-water-polo":{"a":"Woman Playing Water Polo","b":"1F93D-200D-2640-FE0F","j":["water polo","woman","sports","pool"]},"person-playing-handball":{"a":"Person Playing Handball","b":"1F93E","j":["ball","handball","sport"]},"man-playing-handball":{"a":"Man Playing Handball","b":"1F93E-200D-2642-FE0F","j":["handball","man","sports"]},"woman-playing-handball":{"a":"Woman Playing Handball","b":"1F93E-200D-2640-FE0F","j":["handball","woman","sports"]},"person-juggling":{"a":"Person Juggling","b":"1F939","j":["balance","juggle","multitask","skill","performance"]},"man-juggling":{"a":"Man Juggling","b":"1F939-200D-2642-FE0F","j":["juggling","man","multitask","juggle","balance","skill"]},"woman-juggling":{"a":"Woman Juggling","b":"1F939-200D-2640-FE0F","j":["juggling","multitask","woman","juggle","balance","skill"]},"person-in-lotus-position":{"a":"Person in Lotus Position","b":"1F9D8","j":["meditation","yoga","serenity","meditate"]},"man-in-lotus-position":{"a":"Man in Lotus Position","b":"1F9D8-200D-2642-FE0F","j":["meditation","yoga","man","male","serenity","zen","mindfulness"]},"woman-in-lotus-position":{"a":"Woman in Lotus Position","b":"1F9D8-200D-2640-FE0F","j":["meditation","yoga","woman","female","serenity","zen","mindfulness"]},"person-taking-bath":{"a":"Person Taking Bath","b":"1F6C0","j":["bath","bathtub","clean","shower","bathroom"]},"person-in-bed":{"a":"Person in Bed","b":"1F6CC","j":["good night","hotel","sleep","bed","rest"]},"people-holding-hands":{"a":"People Holding Hands","b":"1F9D1-200D-1F91D-200D-1F9D1","j":["couple","hand","hold","holding hands","person","friendship"]},"women-holding-hands":{"a":"Women Holding Hands","b":"1F46D","j":["couple","hand","holding hands","women","pair","friendship","love","like","female","people","human"]},"woman-and-man-holding-hands":{"a":"Woman and Man Holding Hands","b":"1F46B","j":["couple","hand","hold","holding hands","man","woman","pair","people","human","love","date","dating","like","affection","valentines","marriage"]},"men-holding-hands":{"a":"Men Holding Hands","b":"1F46C","j":["couple","Gemini","holding hands","man","men","twins","zodiac","pair","love","like","bromance","friendship","people","human"]},"kiss":{"a":"Kiss","b":"1F48F","j":["couple","pair","valentines","love","like","dating","marriage"]},"kiss-woman-man":{"a":"Kiss: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","woman","love"]},"kiss-man-man":{"a":"Kiss: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","j":["couple","kiss","man","pair","valentines","love","like","dating","marriage"]},"kiss-woman-woman":{"a":"Kiss: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","j":["couple","kiss","woman","pair","valentines","love","like","dating","marriage"]},"couple-with-heart":{"a":"Couple with Heart","b":"1F491","j":["couple","love","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-man":{"a":"Couple with Heart: Woman, Man","b":"1F469-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","woman"]},"couple-with-heart-man-man":{"a":"Couple with Heart: Man, Man","b":"1F468-200D-2764-FE0F-200D-1F468","j":["couple","couple with heart","love","man","pair","like","affection","human","dating","valentines","marriage"]},"couple-with-heart-woman-woman":{"a":"Couple with Heart: Woman, Woman","b":"1F469-200D-2764-FE0F-200D-1F469","j":["couple","couple with heart","love","woman","pair","like","affection","human","dating","valentines","marriage"]},"family":{"a":"Family","b":"1F46A","j":["home","parents","child","mom","dad","father","mother","people","human"]},"family-man-woman-boy":{"a":"Family: Man, Woman, Boy","b":"1F468-200D-1F469-200D-1F466","j":["boy","family","man","woman","love"]},"family-man-woman-girl":{"a":"Family: Man, Woman, Girl","b":"1F468-200D-1F469-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","child"]},"family-man-woman-girl-boy":{"a":"Family: Man, Woman, Girl, Boy","b":"1F468-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","man","woman","home","parents","people","human","children"]},"family-man-woman-boy-boy":{"a":"Family: Man, Woman, Boy, Boy","b":"1F468-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","man","woman","home","parents","people","human","children"]},"family-man-woman-girl-girl":{"a":"Family: Man, Woman, Girl, Girl","b":"1F468-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","man","woman","home","parents","people","human","children"]},"family-man-man-boy":{"a":"Family: Man, Man, Boy","b":"1F468-200D-1F468-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl":{"a":"Family: Man, Man, Girl","b":"1F468-200D-1F468-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-man-man-girl-boy":{"a":"Family: Man, Man, Girl, Boy","b":"1F468-200D-1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parents","people","human","children"]},"family-man-man-boy-boy":{"a":"Family: Man, Man, Boy, Boy","b":"1F468-200D-1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parents","people","human","children"]},"family-man-man-girl-girl":{"a":"Family: Man, Man, Girl, Girl","b":"1F468-200D-1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parents","people","human","children"]},"family-woman-woman-boy":{"a":"Family: Woman, Woman, Boy","b":"1F469-200D-1F469-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl":{"a":"Family: Woman, Woman, Girl","b":"1F469-200D-1F469-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-girl-boy":{"a":"Family: Woman, Woman, Girl, Boy","b":"1F469-200D-1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parents","people","human","children"]},"family-woman-woman-boy-boy":{"a":"Family: Woman, Woman, Boy, Boy","b":"1F469-200D-1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parents","people","human","children"]},"family-woman-woman-girl-girl":{"a":"Family: Woman, Woman, Girl, Girl","b":"1F469-200D-1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parents","people","human","children"]},"family-man-boy":{"a":"Family: Man, Boy","b":"1F468-200D-1F466","j":["boy","family","man","home","parent","people","human","child"]},"family-man-boy-boy":{"a":"Family: Man, Boy, Boy","b":"1F468-200D-1F466-200D-1F466","j":["boy","family","man","home","parent","people","human","children"]},"family-man-girl":{"a":"Family: Man, Girl","b":"1F468-200D-1F467","j":["family","girl","man","home","parent","people","human","child"]},"family-man-girl-boy":{"a":"Family: Man, Girl, Boy","b":"1F468-200D-1F467-200D-1F466","j":["boy","family","girl","man","home","parent","people","human","children"]},"family-man-girl-girl":{"a":"Family: Man, Girl, Girl","b":"1F468-200D-1F467-200D-1F467","j":["family","girl","man","home","parent","people","human","children"]},"family-woman-boy":{"a":"Family: Woman, Boy","b":"1F469-200D-1F466","j":["boy","family","woman","home","parent","people","human","child"]},"family-woman-boy-boy":{"a":"Family: Woman, Boy, Boy","b":"1F469-200D-1F466-200D-1F466","j":["boy","family","woman","home","parent","people","human","children"]},"family-woman-girl":{"a":"Family: Woman, Girl","b":"1F469-200D-1F467","j":["family","girl","woman","home","parent","people","human","child"]},"family-woman-girl-boy":{"a":"Family: Woman, Girl, Boy","b":"1F469-200D-1F467-200D-1F466","j":["boy","family","girl","woman","home","parent","people","human","children"]},"family-woman-girl-girl":{"a":"Family: Woman, Girl, Girl","b":"1F469-200D-1F467-200D-1F467","j":["family","girl","woman","home","parent","people","human","children"]},"speaking-head":{"a":"Speaking Head","b":"1F5E3","j":["face","head","silhouette","speak","speaking","user","person","human","sing","say","talk"]},"bust-in-silhouette":{"a":"Bust in Silhouette","b":"1F464","j":["bust","silhouette","user","person","human"]},"busts-in-silhouette":{"a":"Busts in Silhouette","b":"1F465","j":["bust","silhouette","user","person","human","group","team"]},"people-hugging":{"a":"People Hugging","b":"1FAC2","j":["goodbye","hello","hug","thanks","care"]},"footprints":{"a":"Footprints","b":"1F463","j":["clothing","footprint","print","feet","tracking","walking","beach"]},"red-hair":{"a":"Red Hair","b":"1F9B0","j":["ginger","red hair","redhead"]},"curly-hair":{"a":"Curly Hair","b":"1F9B1","j":["afro","curly","curly hair","ringlets"]},"white-hair":{"a":"White Hair","b":"1F9B3","j":["gray","hair","old","white"]},"bald":{"a":"Bald","b":"1F9B2","j":["bald","chemotherapy","hairless","no hair","shaven"]},"monkey-face":{"a":"Monkey Face","b":"1F435","j":["face","monkey","animal","nature","circus"]},"monkey":{"a":"Monkey","b":"1F412","j":["animal","nature","banana","circus"]},"gorilla":{"a":"Gorilla","b":"1F98D","j":["animal","nature","circus"]},"orangutan":{"a":"Orangutan","b":"1F9A7","j":["ape","animal"]},"dog-face":{"a":"Dog Face","b":"1F436","j":["dog","face","pet","animal","friend","nature","woof","puppy","faithful"]},"dog":{"a":"Dog","b":"1F415","j":["pet","animal","nature","friend","doge","faithful"]},"guide-dog":{"a":"Guide Dog","b":"1F9AE","j":["accessibility","blind","guide","animal"]},"service-dog":{"a":"Service Dog","b":"1F415-200D-1F9BA","j":["accessibility","assistance","dog","service","blind","animal"]},"poodle":{"a":"Poodle","b":"1F429","j":["dog","animal","101","nature","pet"]},"wolf":{"a":"Wolf","b":"1F43A","j":["face","animal","nature","wild"]},"fox":{"a":"Fox","b":"1F98A","j":["face","animal","nature"]},"raccoon":{"a":"Raccoon","b":"1F99D","j":["curious","sly","animal","nature"]},"cat-face":{"a":"Cat Face","b":"1F431","j":["cat","face","pet","animal","meow","nature","kitten"]},"cat":{"a":"Cat","b":"1F408","j":["pet","animal","meow","cats"]},"black-cat":{"a":"Black Cat","b":"1F408-200D-2B1B","j":["black","cat","unlucky","superstition","luck"]},"lion":{"a":"Lion","b":"1F981","j":["face","Leo","zodiac","animal","nature"]},"tiger-face":{"a":"Tiger Face","b":"1F42F","j":["face","tiger","animal","cat","danger","wild","nature","roar"]},"tiger":{"a":"Tiger","b":"1F405","j":["animal","nature","roar"]},"leopard":{"a":"Leopard","b":"1F406","j":["animal","nature"]},"horse-face":{"a":"Horse Face","b":"1F434","j":["face","horse","animal","brown","nature"]},"moose":{"a":"⊛ Moose","b":"1FACE","j":["animal","antlers","elk","mammal","moose"]},"donkey":{"a":"⊛ Donkey","b":"1FACF","j":["animal","ass","burro","donkey","mammal","mule","stubborn"]},"horse":{"a":"Horse","b":"1F40E","j":["equestrian","racehorse","racing","animal","gamble","luck"]},"unicorn":{"a":"Unicorn","b":"1F984","j":["face","animal","nature","mystical"]},"zebra":{"a":"Zebra","b":"1F993","j":["stripe","animal","nature","stripes","safari"]},"deer":{"a":"Deer","b":"1F98C","j":["animal","nature","horns","venison"]},"bison":{"a":"Bison","b":"1F9AC","j":["buffalo","herd","wisent","ox"]},"cow-face":{"a":"Cow Face","b":"1F42E","j":["cow","face","beef","ox","animal","nature","moo","milk"]},"ox":{"a":"Ox","b":"1F402","j":["bull","Taurus","zodiac","animal","cow","beef"]},"water-buffalo":{"a":"Water Buffalo","b":"1F403","j":["buffalo","water","animal","nature","ox","cow"]},"cow":{"a":"Cow","b":"1F404","j":["beef","ox","animal","nature","moo","milk"]},"pig-face":{"a":"Pig Face","b":"1F437","j":["face","pig","animal","oink","nature"]},"pig":{"a":"Pig","b":"1F416","j":["sow","animal","nature"]},"boar":{"a":"Boar","b":"1F417","j":["pig","animal","nature"]},"pig-nose":{"a":"Pig Nose","b":"1F43D","j":["face","nose","pig","animal","oink"]},"ram":{"a":"Ram","b":"1F40F","j":["Aries","male","sheep","zodiac","animal","nature"]},"ewe":{"a":"Ewe","b":"1F411","j":["female","sheep","animal","nature","wool","shipit"]},"goat":{"a":"Goat","b":"1F410","j":["Capricorn","zodiac","animal","nature"]},"camel":{"a":"Camel","b":"1F42A","j":["dromedary","hump","animal","hot","desert"]},"twohump-camel":{"a":"Two-Hump Camel","b":"1F42B","j":["bactrian","camel","hump","two-hump camel","two_hump_camel","animal","nature","hot","desert"]},"llama":{"a":"Llama","b":"1F999","j":["alpaca","guanaco","vicuña","wool","animal","nature"]},"giraffe":{"a":"Giraffe","b":"1F992","j":["spots","animal","nature","safari"]},"elephant":{"a":"Elephant","b":"1F418","j":["animal","nature","nose","th","circus"]},"mammoth":{"a":"Mammoth","b":"1F9A3","j":["extinction","large","tusk","woolly","elephant","tusks"]},"rhinoceros":{"a":"Rhinoceros","b":"1F98F","j":["animal","nature","horn"]},"hippopotamus":{"a":"Hippopotamus","b":"1F99B","j":["hippo","animal","nature"]},"mouse-face":{"a":"Mouse Face","b":"1F42D","j":["face","mouse","animal","nature","cheese_wedge","rodent"]},"mouse":{"a":"Mouse","b":"1F401","j":["animal","nature","rodent"]},"rat":{"a":"Rat","b":"1F400","j":["animal","mouse","rodent"]},"hamster":{"a":"Hamster","b":"1F439","j":["face","pet","animal","nature"]},"rabbit-face":{"a":"Rabbit Face","b":"1F430","j":["bunny","face","pet","rabbit","animal","nature","spring","magic"]},"rabbit":{"a":"Rabbit","b":"1F407","j":["bunny","pet","animal","nature","magic","spring"]},"chipmunk":{"a":"Chipmunk","b":"1F43F","j":["squirrel","animal","nature","rodent"]},"beaver":{"a":"Beaver","b":"1F9AB","j":["dam","animal","rodent"]},"hedgehog":{"a":"Hedgehog","b":"1F994","j":["spiny","animal","nature"]},"bat":{"a":"Bat","b":"1F987","j":["vampire","animal","nature","blind"]},"bear":{"a":"Bear","b":"1F43B","j":["face","animal","nature","wild"]},"polar-bear":{"a":"Polar Bear","b":"1F43B-200D-2744-FE0F","j":["arctic","bear","white","animal"]},"koala":{"a":"Koala","b":"1F428","j":["face","marsupial","animal","nature"]},"panda":{"a":"Panda","b":"1F43C","j":["face","animal","nature"]},"sloth":{"a":"Sloth","b":"1F9A5","j":["lazy","slow","animal"]},"otter":{"a":"Otter","b":"1F9A6","j":["fishing","playful","animal"]},"skunk":{"a":"Skunk","b":"1F9A8","j":["stink","animal"]},"kangaroo":{"a":"Kangaroo","b":"1F998","j":["Australia","joey","jump","marsupial","animal","nature","australia","hop"]},"badger":{"a":"Badger","b":"1F9A1","j":["honey badger","pester","animal","nature","honey"]},"paw-prints":{"a":"Paw Prints","b":"1F43E","j":["feet","paw","print","animal","tracking","footprints","dog","cat","pet"]},"turkey":{"a":"Turkey","b":"1F983","j":["bird","animal"]},"chicken":{"a":"Chicken","b":"1F414","j":["bird","animal","cluck","nature"]},"rooster":{"a":"Rooster","b":"1F413","j":["bird","animal","nature","chicken"]},"hatching-chick":{"a":"Hatching Chick","b":"1F423","j":["baby","bird","chick","hatching","animal","chicken","egg","born"]},"baby-chick":{"a":"Baby Chick","b":"1F424","j":["baby","bird","chick","animal","chicken"]},"frontfacing-baby-chick":{"a":"Front-Facing Baby Chick","b":"1F425","j":["baby","bird","chick","front-facing baby chick","front_facing_baby_chick","animal","chicken"]},"bird":{"a":"Bird","b":"1F426","j":["animal","nature","fly","tweet","spring"]},"penguin":{"a":"Penguin","b":"1F427","j":["bird","animal","nature"]},"dove":{"a":"Dove","b":"1F54A","j":["bird","fly","peace","animal"]},"eagle":{"a":"Eagle","b":"1F985","j":["bird","animal","nature"]},"duck":{"a":"Duck","b":"1F986","j":["bird","animal","nature","mallard"]},"swan":{"a":"Swan","b":"1F9A2","j":["bird","cygnet","ugly duckling","animal","nature"]},"owl":{"a":"Owl","b":"1F989","j":["bird","wise","animal","nature","hoot"]},"dodo":{"a":"Dodo","b":"1F9A4","j":["extinction","large","Mauritius","animal","bird"]},"feather":{"a":"Feather","b":"1FAB6","j":["bird","flight","light","plumage","fly"]},"flamingo":{"a":"Flamingo","b":"1F9A9","j":["flamboyant","tropical","animal"]},"peacock":{"a":"Peacock","b":"1F99A","j":["bird","ostentatious","peahen","proud","animal","nature"]},"parrot":{"a":"Parrot","b":"1F99C","j":["bird","pirate","talk","animal","nature"]},"wing":{"a":"⊛ Wing","b":"1FABD","j":["angelic","aviation","bird","flying","mythology","wing"]},"black-bird":{"a":"⊛ Black Bird","b":"1F426-200D-2B1B","j":["bird","black","crow","raven","rook"]},"goose":{"a":"⊛ Goose","b":"1FABF","j":["bird","fowl","goose","honk","silly"]},"frog":{"a":"Frog","b":"1F438","j":["face","animal","nature","croak","toad"]},"crocodile":{"a":"Crocodile","b":"1F40A","j":["animal","nature","reptile","lizard","alligator"]},"turtle":{"a":"Turtle","b":"1F422","j":["terrapin","tortoise","animal","slow","nature"]},"lizard":{"a":"Lizard","b":"1F98E","j":["reptile","animal","nature"]},"snake":{"a":"Snake","b":"1F40D","j":["bearer","Ophiuchus","serpent","zodiac","animal","evil","nature","hiss","python"]},"dragon-face":{"a":"Dragon Face","b":"1F432","j":["dragon","face","fairy tale","animal","myth","nature","chinese","green"]},"dragon":{"a":"Dragon","b":"1F409","j":["fairy tale","animal","myth","nature","chinese","green"]},"sauropod":{"a":"Sauropod","b":"1F995","j":["brachiosaurus","brontosaurus","diplodocus","animal","nature","dinosaur","extinct"]},"trex":{"a":"T-Rex","b":"1F996","j":["Tyrannosaurus Rex","t_rex","animal","nature","dinosaur","tyrannosaurus","extinct"]},"spouting-whale":{"a":"Spouting Whale","b":"1F433","j":["face","spouting","whale","animal","nature","sea","ocean"]},"whale":{"a":"Whale","b":"1F40B","j":["animal","nature","sea","ocean"]},"dolphin":{"a":"Dolphin","b":"1F42C","j":["flipper","animal","nature","fish","sea","ocean","fins","beach"]},"seal":{"a":"Seal","b":"1F9AD","j":["sea lion","animal","creature","sea"]},"fish":{"a":"Fish","b":"1F41F","j":["Pisces","zodiac","animal","food","nature"]},"tropical-fish":{"a":"Tropical Fish","b":"1F420","j":["fish","tropical","animal","swim","ocean","beach","nemo"]},"blowfish":{"a":"Blowfish","b":"1F421","j":["fish","animal","nature","food","sea","ocean"]},"shark":{"a":"Shark","b":"1F988","j":["fish","animal","nature","sea","ocean","jaws","fins","beach"]},"octopus":{"a":"Octopus","b":"1F419","j":["animal","creature","ocean","sea","nature","beach"]},"spiral-shell":{"a":"Spiral Shell","b":"1F41A","j":["shell","spiral","nature","sea","beach"]},"coral":{"a":"Coral","b":"1FAB8","j":["ocean","reef","sea"]},"jellyfish":{"a":"⊛ Jellyfish","b":"1FABC","j":["burn","invertebrate","jelly","jellyfish","marine","ouch","stinger"]},"snail":{"a":"Snail","b":"1F40C","j":["slow","animal","shell"]},"butterfly":{"a":"Butterfly","b":"1F98B","j":["insect","pretty","animal","nature","caterpillar"]},"bug":{"a":"Bug","b":"1F41B","j":["insect","animal","nature","worm"]},"ant":{"a":"Ant","b":"1F41C","j":["insect","animal","nature","bug"]},"honeybee":{"a":"Honeybee","b":"1F41D","j":["bee","insect","animal","nature","bug","spring","honey"]},"beetle":{"a":"Beetle","b":"1FAB2","j":["bug","insect"]},"lady-beetle":{"a":"Lady Beetle","b":"1F41E","j":["beetle","insect","ladybird","ladybug","animal","nature"]},"cricket":{"a":"Cricket","b":"1F997","j":["grasshopper","Orthoptera","animal","chirp"]},"cockroach":{"a":"Cockroach","b":"1FAB3","j":["insect","pest","roach","pests"]},"spider":{"a":"Spider","b":"1F577","j":["insect","animal","arachnid"]},"spider-web":{"a":"Spider Web","b":"1F578","j":["spider","web","animal","insect","arachnid","silk"]},"scorpion":{"a":"Scorpion","b":"1F982","j":["scorpio","Scorpio","zodiac","animal","arachnid"]},"mosquito":{"a":"Mosquito","b":"1F99F","j":["disease","fever","malaria","pest","virus","animal","nature","insect"]},"fly":{"a":"Fly","b":"1FAB0","j":["disease","maggot","pest","rotting","insect"]},"worm":{"a":"Worm","b":"1FAB1","j":["annelid","earthworm","parasite","animal"]},"microbe":{"a":"Microbe","b":"1F9A0","j":["amoeba","bacteria","virus","germs","covid"]},"bouquet":{"a":"Bouquet","b":"1F490","j":["flower","flowers","nature","spring"]},"cherry-blossom":{"a":"Cherry Blossom","b":"1F338","j":["blossom","cherry","flower","nature","plant","spring"]},"white-flower":{"a":"White Flower","b":"1F4AE","j":["flower","japanese","spring"]},"lotus":{"a":"Lotus","b":"1FAB7","j":["Buddhism","flower","Hinduism","India","purity","Vietnam","calm","meditation"]},"rosette":{"a":"Rosette","b":"1F3F5","j":["plant","flower","decoration","military"]},"rose":{"a":"Rose","b":"1F339","j":["flower","flowers","valentines","love","spring"]},"wilted-flower":{"a":"Wilted Flower","b":"1F940","j":["flower","wilted","plant","nature","rose"]},"hibiscus":{"a":"Hibiscus","b":"1F33A","j":["flower","plant","vegetable","flowers","beach"]},"sunflower":{"a":"Sunflower","b":"1F33B","j":["flower","sun","nature","plant","fall"]},"blossom":{"a":"Blossom","b":"1F33C","j":["flower","nature","flowers","yellow"]},"tulip":{"a":"Tulip","b":"1F337","j":["flower","flowers","plant","nature","summer","spring"]},"hyacinth":{"a":"⊛ Hyacinth","b":"1FABB","j":["bluebonnet","flower","hyacinth","lavender","lupine","snapdragon"]},"seedling":{"a":"Seedling","b":"1F331","j":["young","plant","nature","grass","lawn","spring"]},"potted-plant":{"a":"Potted Plant","b":"1FAB4","j":["boring","grow","house","nurturing","plant","useless","greenery"]},"evergreen-tree":{"a":"Evergreen Tree","b":"1F332","j":["tree","plant","nature"]},"deciduous-tree":{"a":"Deciduous Tree","b":"1F333","j":["deciduous","shedding","tree","plant","nature"]},"palm-tree":{"a":"Palm Tree","b":"1F334","j":["palm","tree","plant","vegetable","nature","summer","beach","mojito","tropical"]},"cactus":{"a":"Cactus","b":"1F335","j":["plant","vegetable","nature"]},"sheaf-of-rice":{"a":"Sheaf of Rice","b":"1F33E","j":["ear","grain","rice","nature","plant"]},"herb":{"a":"Herb","b":"1F33F","j":["leaf","vegetable","plant","medicine","weed","grass","lawn"]},"shamrock":{"a":"Shamrock","b":"2618","j":["plant","vegetable","nature","irish","clover"]},"four-leaf-clover":{"a":"Four Leaf Clover","b":"1F340","j":["4","clover","four","four-leaf clover","leaf","vegetable","plant","nature","lucky","irish"]},"maple-leaf":{"a":"Maple Leaf","b":"1F341","j":["falling","leaf","maple","nature","plant","vegetable","ca","fall"]},"fallen-leaf":{"a":"Fallen Leaf","b":"1F342","j":["falling","leaf","nature","plant","vegetable","leaves"]},"leaf-fluttering-in-wind":{"a":"Leaf Fluttering in Wind","b":"1F343","j":["blow","flutter","leaf","wind","nature","plant","tree","vegetable","grass","lawn","spring"]},"empty-nest":{"a":"Empty Nest","b":"1FAB9","j":["nesting","bird"]},"nest-with-eggs":{"a":"Nest with Eggs","b":"1FABA","j":["nesting","bird"]},"mushroom":{"a":"Mushroom","b":"1F344","j":["toadstool","plant","vegetable"]},"grapes":{"a":"Grapes","b":"1F347","j":["fruit","grape","food","wine"]},"melon":{"a":"Melon","b":"1F348","j":["fruit","nature","food"]},"watermelon":{"a":"Watermelon","b":"1F349","j":["fruit","food","picnic","summer"]},"tangerine":{"a":"Tangerine","b":"1F34A","j":["fruit","orange","food","nature"]},"lemon":{"a":"Lemon","b":"1F34B","j":["citrus","fruit","nature"]},"banana":{"a":"Banana","b":"1F34C","j":["fruit","food","monkey"]},"pineapple":{"a":"Pineapple","b":"1F34D","j":["fruit","nature","food"]},"mango":{"a":"Mango","b":"1F96D","j":["fruit","tropical","food"]},"red-apple":{"a":"Red Apple","b":"1F34E","j":["apple","fruit","red","mac","school"]},"green-apple":{"a":"Green Apple","b":"1F34F","j":["apple","fruit","green","nature"]},"pear":{"a":"Pear","b":"1F350","j":["fruit","nature","food"]},"peach":{"a":"Peach","b":"1F351","j":["fruit","nature","food"]},"cherries":{"a":"Cherries","b":"1F352","j":["berries","cherry","fruit","red","food"]},"strawberry":{"a":"Strawberry","b":"1F353","j":["berry","fruit","food","nature"]},"blueberries":{"a":"Blueberries","b":"1FAD0","j":["berry","bilberry","blue","blueberry","fruit"]},"kiwi-fruit":{"a":"Kiwi Fruit","b":"1F95D","j":["food","fruit","kiwi"]},"tomato":{"a":"Tomato","b":"1F345","j":["fruit","vegetable","nature","food"]},"olive":{"a":"Olive","b":"1FAD2","j":["food","fruit"]},"coconut":{"a":"Coconut","b":"1F965","j":["palm","piña colada","fruit","nature","food"]},"avocado":{"a":"Avocado","b":"1F951","j":["food","fruit"]},"eggplant":{"a":"Eggplant","b":"1F346","j":["aubergine","vegetable","nature","food"]},"potato":{"a":"Potato","b":"1F954","j":["food","vegetable","tuber","vegatable","starch"]},"carrot":{"a":"Carrot","b":"1F955","j":["food","vegetable","orange"]},"ear-of-corn":{"a":"Ear of Corn","b":"1F33D","j":["corn","ear","maize","maze","food","vegetable","plant"]},"hot-pepper":{"a":"Hot Pepper","b":"1F336","j":["hot","pepper","food","spicy","chilli","chili"]},"bell-pepper":{"a":"Bell Pepper","b":"1FAD1","j":["capsicum","pepper","vegetable","fruit","plant"]},"cucumber":{"a":"Cucumber","b":"1F952","j":["food","pickle","vegetable","fruit"]},"leafy-green":{"a":"Leafy Green","b":"1F96C","j":["bok choy","cabbage","kale","lettuce","food","vegetable","plant"]},"broccoli":{"a":"Broccoli","b":"1F966","j":["wild cabbage","fruit","food","vegetable"]},"garlic":{"a":"Garlic","b":"1F9C4","j":["flavoring","food","spice","cook"]},"onion":{"a":"Onion","b":"1F9C5","j":["flavoring","cook","food","spice"]},"peanuts":{"a":"Peanuts","b":"1F95C","j":["food","nut","peanut","vegetable"]},"beans":{"a":"Beans","b":"1FAD8","j":["food","kidney","legume"]},"chestnut":{"a":"Chestnut","b":"1F330","j":["plant","food","squirrel"]},"ginger-root":{"a":"⊛ Ginger Root","b":"1FADA","j":["beer","ginger root","root","spice"]},"pea-pod":{"a":"⊛ Pea Pod","b":"1FADB","j":["beans","edamame","legume","pea","pod","vegetable"]},"bread":{"a":"Bread","b":"1F35E","j":["loaf","food","wheat","breakfast","toast"]},"croissant":{"a":"Croissant","b":"1F950","j":["bread","breakfast","food","french","roll"]},"baguette-bread":{"a":"Baguette Bread","b":"1F956","j":["baguette","bread","food","french","france","bakery"]},"flatbread":{"a":"Flatbread","b":"1FAD3","j":["arepa","lavash","naan","pita","flour","food","bakery"]},"pretzel":{"a":"Pretzel","b":"1F968","j":["twisted","convoluted","food","bread","germany","bakery"]},"bagel":{"a":"Bagel","b":"1F96F","j":["bakery","breakfast","schmear","food","bread","jewish"]},"pancakes":{"a":"Pancakes","b":"1F95E","j":["breakfast","crêpe","food","hotcake","pancake","flapjacks","hotcakes","brunch"]},"waffle":{"a":"Waffle","b":"1F9C7","j":["breakfast","indecisive","iron","food","brunch"]},"cheese-wedge":{"a":"Cheese Wedge","b":"1F9C0","j":["cheese","food","chadder","swiss"]},"meat-on-bone":{"a":"Meat on Bone","b":"1F356","j":["bone","meat","good","food","drumstick"]},"poultry-leg":{"a":"Poultry Leg","b":"1F357","j":["bone","chicken","drumstick","leg","poultry","food","meat","bird","turkey"]},"cut-of-meat":{"a":"Cut of Meat","b":"1F969","j":["chop","lambchop","porkchop","steak","food","cow","meat","cut"]},"bacon":{"a":"Bacon","b":"1F953","j":["breakfast","food","meat","pork","pig","brunch"]},"hamburger":{"a":"Hamburger","b":"1F354","j":["burger","meat","fast food","beef","cheeseburger","mcdonalds","burger king"]},"french-fries":{"a":"French Fries","b":"1F35F","j":["french","fries","chips","snack","fast food","potato"]},"pizza":{"a":"Pizza","b":"1F355","j":["cheese","slice","food","party","italy"]},"hot-dog":{"a":"Hot Dog","b":"1F32D","j":["frankfurter","hotdog","sausage","food","america"]},"sandwich":{"a":"Sandwich","b":"1F96A","j":["bread","food","lunch","toast","bakery"]},"taco":{"a":"Taco","b":"1F32E","j":["mexican","food"]},"burrito":{"a":"Burrito","b":"1F32F","j":["mexican","wrap","food"]},"tamale":{"a":"Tamale","b":"1FAD4","j":["mexican","wrapped","food","masa"]},"stuffed-flatbread":{"a":"Stuffed Flatbread","b":"1F959","j":["falafel","flatbread","food","gyro","kebab","stuffed","mediterranean"]},"falafel":{"a":"Falafel","b":"1F9C6","j":["chickpea","meatball","food","mediterranean"]},"egg":{"a":"Egg","b":"1F95A","j":["breakfast","food","chicken"]},"cooking":{"a":"Cooking","b":"1F373","j":["breakfast","egg","frying","pan","food","kitchen","skillet"]},"shallow-pan-of-food":{"a":"Shallow Pan of Food","b":"1F958","j":["casserole","food","paella","pan","shallow","cooking","skillet"]},"pot-of-food":{"a":"Pot of Food","b":"1F372","j":["pot","stew","food","meat","soup","hot pot"]},"fondue":{"a":"Fondue","b":"1FAD5","j":["cheese","chocolate","melted","pot","Swiss","food"]},"bowl-with-spoon":{"a":"Bowl with Spoon","b":"1F963","j":["breakfast","cereal","congee","oatmeal","porridge","food"]},"green-salad":{"a":"Green Salad","b":"1F957","j":["food","green","salad","healthy","lettuce","vegetable"]},"popcorn":{"a":"Popcorn","b":"1F37F","j":["food","movie theater","films","snack","drama"]},"butter":{"a":"Butter","b":"1F9C8","j":["dairy","food","cook"]},"salt":{"a":"Salt","b":"1F9C2","j":["condiment","shaker"]},"canned-food":{"a":"Canned Food","b":"1F96B","j":["can","food","soup","tomatoes"]},"bento-box":{"a":"Bento Box","b":"1F371","j":["bento","box","food","japanese","lunch"]},"rice-cracker":{"a":"Rice Cracker","b":"1F358","j":["cracker","rice","food","japanese","snack"]},"rice-ball":{"a":"Rice Ball","b":"1F359","j":["ball","Japanese","rice","food","japanese"]},"cooked-rice":{"a":"Cooked Rice","b":"1F35A","j":["cooked","rice","food","asian"]},"curry-rice":{"a":"Curry Rice","b":"1F35B","j":["curry","rice","food","spicy","hot","indian"]},"steaming-bowl":{"a":"Steaming Bowl","b":"1F35C","j":["bowl","noodle","ramen","steaming","food","japanese","chopsticks"]},"spaghetti":{"a":"Spaghetti","b":"1F35D","j":["pasta","food","italian","noodle"]},"roasted-sweet-potato":{"a":"Roasted Sweet Potato","b":"1F360","j":["potato","roasted","sweet","food","nature","plant"]},"oden":{"a":"Oden","b":"1F362","j":["kebab","seafood","skewer","stick","food","japanese"]},"sushi":{"a":"Sushi","b":"1F363","j":["food","fish","japanese","rice"]},"fried-shrimp":{"a":"Fried Shrimp","b":"1F364","j":["fried","prawn","shrimp","tempura","food","animal","appetizer","summer"]},"fish-cake-with-swirl":{"a":"Fish Cake with Swirl","b":"1F365","j":["cake","fish","pastry","swirl","food","japan","sea","beach","narutomaki","pink","kamaboko","surimi","ramen"]},"moon-cake":{"a":"Moon Cake","b":"1F96E","j":["autumn","festival","yuèbǐng","food","dessert"]},"dango":{"a":"Dango","b":"1F361","j":["dessert","Japanese","skewer","stick","sweet","food","japanese","barbecue","meat"]},"dumpling":{"a":"Dumpling","b":"1F95F","j":["empanada","gyōza","jiaozi","pierogi","potsticker","food","gyoza"]},"fortune-cookie":{"a":"Fortune Cookie","b":"1F960","j":["prophecy","food","dessert"]},"takeout-box":{"a":"Takeout Box","b":"1F961","j":["oyster pail","food","leftovers"]},"crab":{"a":"Crab","b":"1F980","j":["Cancer","zodiac","animal","crustacean"]},"lobster":{"a":"Lobster","b":"1F99E","j":["bisque","claws","seafood","animal","nature"]},"shrimp":{"a":"Shrimp","b":"1F990","j":["food","shellfish","small","animal","ocean","nature","seafood"]},"squid":{"a":"Squid","b":"1F991","j":["food","molusc","animal","nature","ocean","sea"]},"oyster":{"a":"Oyster","b":"1F9AA","j":["diving","pearl","food"]},"soft-ice-cream":{"a":"Soft Ice Cream","b":"1F366","j":["cream","dessert","ice","icecream","soft","sweet","food","hot","summer"]},"shaved-ice":{"a":"Shaved Ice","b":"1F367","j":["dessert","ice","shaved","sweet","hot","summer"]},"ice-cream":{"a":"Ice Cream","b":"1F368","j":["cream","dessert","ice","sweet","food","hot"]},"doughnut":{"a":"Doughnut","b":"1F369","j":["breakfast","dessert","donut","sweet","food","snack"]},"cookie":{"a":"Cookie","b":"1F36A","j":["dessert","sweet","food","snack","oreo","chocolate"]},"birthday-cake":{"a":"Birthday Cake","b":"1F382","j":["birthday","cake","celebration","dessert","pastry","sweet","food"]},"shortcake":{"a":"Shortcake","b":"1F370","j":["cake","dessert","pastry","slice","sweet","food"]},"cupcake":{"a":"Cupcake","b":"1F9C1","j":["bakery","sweet","food","dessert"]},"pie":{"a":"Pie","b":"1F967","j":["filling","pastry","fruit","meat","food","dessert"]},"chocolate-bar":{"a":"Chocolate Bar","b":"1F36B","j":["bar","chocolate","dessert","sweet","food","snack"]},"candy":{"a":"Candy","b":"1F36C","j":["dessert","sweet","snack","lolly"]},"lollipop":{"a":"Lollipop","b":"1F36D","j":["candy","dessert","sweet","food","snack"]},"custard":{"a":"Custard","b":"1F36E","j":["dessert","pudding","sweet","food"]},"honey-pot":{"a":"Honey Pot","b":"1F36F","j":["honey","honeypot","pot","sweet","bees","kitchen"]},"baby-bottle":{"a":"Baby Bottle","b":"1F37C","j":["baby","bottle","drink","milk","food","container"]},"glass-of-milk":{"a":"Glass of Milk","b":"1F95B","j":["drink","glass","milk","beverage","cow"]},"hot-beverage":{"a":"Hot Beverage","b":"2615","j":["beverage","coffee","drink","hot","steaming","tea","caffeine","latte","espresso","mug"]},"teapot":{"a":"Teapot","b":"1FAD6","j":["drink","pot","tea","hot"]},"teacup-without-handle":{"a":"Teacup Without Handle","b":"1F375","j":["beverage","cup","drink","tea","teacup","bowl","breakfast","green","british"]},"sake":{"a":"Sake","b":"1F376","j":["bar","beverage","bottle","cup","drink","wine","drunk","japanese","alcohol","booze"]},"bottle-with-popping-cork":{"a":"Bottle with Popping Cork","b":"1F37E","j":["bar","bottle","cork","drink","popping","wine","celebration"]},"wine-glass":{"a":"Wine Glass","b":"1F377","j":["bar","beverage","drink","glass","wine","drunk","alcohol","booze"]},"cocktail-glass":{"a":"Cocktail Glass","b":"1F378","j":["bar","cocktail","drink","glass","drunk","alcohol","beverage","booze","mojito"]},"tropical-drink":{"a":"Tropical Drink","b":"1F379","j":["bar","drink","tropical","beverage","cocktail","summer","beach","alcohol","booze","mojito"]},"beer-mug":{"a":"Beer Mug","b":"1F37A","j":["bar","beer","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-beer-mugs":{"a":"Clinking Beer Mugs","b":"1F37B","j":["bar","beer","clink","drink","mug","relax","beverage","drunk","party","pub","summer","alcohol","booze"]},"clinking-glasses":{"a":"Clinking Glasses","b":"1F942","j":["celebrate","clink","drink","glass","beverage","party","alcohol","cheers","wine","champagne","toast"]},"tumbler-glass":{"a":"Tumbler Glass","b":"1F943","j":["glass","liquor","shot","tumbler","whisky","drink","beverage","drunk","alcohol","booze","bourbon","scotch"]},"pouring-liquid":{"a":"Pouring Liquid","b":"1FAD7","j":["drink","empty","glass","spill","cup","water"]},"cup-with-straw":{"a":"Cup with Straw","b":"1F964","j":["juice","soda","malt","soft drink","water","drink"]},"bubble-tea":{"a":"Bubble Tea","b":"1F9CB","j":["bubble","milk","pearl","tea","taiwan","boba","milk tea","straw"]},"beverage-box":{"a":"Beverage Box","b":"1F9C3","j":["beverage","box","juice","straw","sweet","drink"]},"mate":{"a":"Mate","b":"1F9C9","j":["drink","tea","beverage"]},"ice":{"a":"Ice","b":"1F9CA","j":["cold","ice cube","iceberg","water"]},"chopsticks":{"a":"Chopsticks","b":"1F962","j":["hashi","jeotgarak","kuaizi","food"]},"fork-and-knife-with-plate":{"a":"Fork and Knife with Plate","b":"1F37D","j":["cooking","fork","knife","plate","food","eat","meal","lunch","dinner","restaurant"]},"fork-and-knife":{"a":"Fork and Knife","b":"1F374","j":["cooking","cutlery","fork","knife","kitchen"]},"spoon":{"a":"Spoon","b":"1F944","j":["tableware","cutlery","kitchen"]},"kitchen-knife":{"a":"Kitchen Knife","b":"1F52A","j":["cooking","hocho","knife","tool","weapon","blade","cutlery","kitchen"]},"jar":{"a":"Jar","b":"1FAD9","j":["condiment","container","empty","sauce","store"]},"amphora":{"a":"Amphora","b":"1F3FA","j":["Aquarius","cooking","drink","jug","zodiac","vase","jar"]},"globe-showing-europeafrica":{"a":"Globe Showing Europe-Africa","b":"1F30D","j":["Africa","earth","Europe","globe","globe showing Europe-Africa","world","globe_showing_europe_africa","international"]},"globe-showing-americas":{"a":"Globe Showing Americas","b":"1F30E","j":["Americas","earth","globe","globe showing Americas","world","USA","international"]},"globe-showing-asiaaustralia":{"a":"Globe Showing Asia-Australia","b":"1F30F","j":["Asia","Australia","earth","globe","globe showing Asia-Australia","world","globe_showing_asia_australia","east","international"]},"globe-with-meridians":{"a":"Globe with Meridians","b":"1F310","j":["earth","globe","meridians","world","international","internet","interweb","i18n"]},"world-map":{"a":"World Map","b":"1F5FA","j":["map","world","location","direction"]},"map-of-japan":{"a":"Map of Japan","b":"1F5FE","j":["Japan","map","map of Japan","nation","country","japanese","asia"]},"compass":{"a":"Compass","b":"1F9ED","j":["magnetic","navigation","orienteering"]},"snowcapped-mountain":{"a":"Snow-Capped Mountain","b":"1F3D4","j":["cold","mountain","snow","snow-capped mountain","snow_capped_mountain","photo","nature","environment","winter"]},"mountain":{"a":"Mountain","b":"26F0","j":["photo","nature","environment"]},"volcano":{"a":"Volcano","b":"1F30B","j":["eruption","mountain","photo","nature","disaster"]},"mount-fuji":{"a":"Mount Fuji","b":"1F5FB","j":["fuji","mountain","photo","nature","japanese"]},"camping":{"a":"Camping","b":"1F3D5","j":["photo","outdoors","tent"]},"beach-with-umbrella":{"a":"Beach with Umbrella","b":"1F3D6","j":["beach","umbrella","weather","summer","sunny","sand","mojito"]},"desert":{"a":"Desert","b":"1F3DC","j":["photo","warm","saharah"]},"desert-island":{"a":"Desert Island","b":"1F3DD","j":["desert","island","photo","tropical","mojito"]},"national-park":{"a":"National Park","b":"1F3DE","j":["park","photo","environment","nature"]},"stadium":{"a":"Stadium","b":"1F3DF","j":["photo","place","sports","concert","venue"]},"classical-building":{"a":"Classical Building","b":"1F3DB","j":["classical","art","culture","history"]},"building-construction":{"a":"Building Construction","b":"1F3D7","j":["construction","wip","working","progress"]},"brick":{"a":"Brick","b":"1F9F1","j":["bricks","clay","mortar","wall"]},"rock":{"a":"Rock","b":"1FAA8","j":["boulder","heavy","solid","stone"]},"wood":{"a":"Wood","b":"1FAB5","j":["log","lumber","timber","nature","trunk"]},"hut":{"a":"Hut","b":"1F6D6","j":["house","roundhouse","yurt","structure"]},"houses":{"a":"Houses","b":"1F3D8","j":["buildings","photo"]},"derelict-house":{"a":"Derelict House","b":"1F3DA","j":["derelict","house","abandon","evict","broken","building"]},"house":{"a":"House","b":"1F3E0","j":["home","building"]},"house-with-garden":{"a":"House with Garden","b":"1F3E1","j":["garden","home","house","plant","nature"]},"office-building":{"a":"Office Building","b":"1F3E2","j":["building","bureau","work"]},"japanese-post-office":{"a":"Japanese Post Office","b":"1F3E3","j":["Japanese","Japanese post office","post","building","envelope","communication"]},"post-office":{"a":"Post Office","b":"1F3E4","j":["European","post","building","email"]},"hospital":{"a":"Hospital","b":"1F3E5","j":["doctor","medicine","building","health","surgery"]},"bank":{"a":"Bank","b":"1F3E6","j":["building","money","sales","cash","business","enterprise"]},"hotel":{"a":"Hotel","b":"1F3E8","j":["building","accomodation","checkin"]},"love-hotel":{"a":"Love Hotel","b":"1F3E9","j":["hotel","love","like","affection","dating"]},"convenience-store":{"a":"Convenience Store","b":"1F3EA","j":["convenience","store","building","shopping","groceries"]},"school":{"a":"School","b":"1F3EB","j":["building","student","education","learn","teach"]},"department-store":{"a":"Department Store","b":"1F3EC","j":["department","store","building","shopping","mall"]},"factory":{"a":"Factory","b":"1F3ED","j":["building","industry","pollution","smoke"]},"japanese-castle":{"a":"Japanese Castle","b":"1F3EF","j":["castle","Japanese","photo","building"]},"castle":{"a":"Castle","b":"1F3F0","j":["European","building","royalty","history"]},"wedding":{"a":"Wedding","b":"1F492","j":["chapel","romance","love","like","affection","couple","marriage","bride","groom"]},"tokyo-tower":{"a":"Tokyo Tower","b":"1F5FC","j":["Tokyo","tower","photo","japanese"]},"statue-of-liberty":{"a":"Statue of Liberty","b":"1F5FD","j":["liberty","statue","american","newyork"]},"church":{"a":"Church","b":"26EA","j":["Christian","cross","religion","building","christ"]},"mosque":{"a":"Mosque","b":"1F54C","j":["islam","Muslim","religion","worship","minaret"]},"hindu-temple":{"a":"Hindu Temple","b":"1F6D5","j":["hindu","temple","religion"]},"synagogue":{"a":"Synagogue","b":"1F54D","j":["Jew","Jewish","religion","temple","judaism","worship","jewish"]},"shinto-shrine":{"a":"Shinto Shrine","b":"26E9","j":["religion","shinto","shrine","temple","japan","kyoto"]},"kaaba":{"a":"Kaaba","b":"1F54B","j":["islam","Muslim","religion","mecca","mosque"]},"fountain":{"a":"Fountain","b":"26F2","j":["photo","summer","water","fresh"]},"tent":{"a":"Tent","b":"26FA","j":["camping","photo","outdoors"]},"foggy":{"a":"Foggy","b":"1F301","j":["fog","photo","mountain"]},"night-with-stars":{"a":"Night with Stars","b":"1F303","j":["night","star","evening","city","downtown"]},"cityscape":{"a":"Cityscape","b":"1F3D9","j":["city","photo","night life","urban"]},"sunrise-over-mountains":{"a":"Sunrise over Mountains","b":"1F304","j":["morning","mountain","sun","sunrise","view","vacation","photo"]},"sunrise":{"a":"Sunrise","b":"1F305","j":["morning","sun","view","vacation","photo"]},"cityscape-at-dusk":{"a":"Cityscape at Dusk","b":"1F306","j":["city","dusk","evening","landscape","sunset","photo","sky","buildings"]},"sunset":{"a":"Sunset","b":"1F307","j":["dusk","sun","photo","good morning","dawn"]},"bridge-at-night":{"a":"Bridge at Night","b":"1F309","j":["bridge","night","photo","sanfrancisco"]},"hot-springs":{"a":"Hot Springs","b":"2668","j":["hot","hotsprings","springs","steaming","bath","warm","relax"]},"carousel-horse":{"a":"Carousel Horse","b":"1F3A0","j":["carousel","horse","photo","carnival"]},"playground-slide":{"a":"Playground Slide","b":"1F6DD","j":["amusement park","play","fun","park"]},"ferris-wheel":{"a":"Ferris Wheel","b":"1F3A1","j":["amusement park","ferris","wheel","photo","carnival","londoneye"]},"roller-coaster":{"a":"Roller Coaster","b":"1F3A2","j":["amusement park","coaster","roller","carnival","playground","photo","fun"]},"barber-pole":{"a":"Barber Pole","b":"1F488","j":["barber","haircut","pole","hair","salon","style"]},"circus-tent":{"a":"Circus Tent","b":"1F3AA","j":["circus","tent","festival","carnival","party"]},"locomotive":{"a":"Locomotive","b":"1F682","j":["engine","railway","steam","train","transportation","vehicle"]},"railway-car":{"a":"Railway Car","b":"1F683","j":["car","electric","railway","train","tram","trolleybus","transportation","vehicle"]},"highspeed-train":{"a":"High-Speed Train","b":"1F684","j":["high-speed train","railway","shinkansen","speed","train","high_speed_train","transportation","vehicle"]},"bullet-train":{"a":"Bullet Train","b":"1F685","j":["bullet","railway","shinkansen","speed","train","transportation","vehicle","fast","public","travel"]},"train":{"a":"Train","b":"1F686","j":["railway","transportation","vehicle"]},"metro":{"a":"Metro","b":"1F687","j":["subway","transportation","blue-square","mrt","underground","tube"]},"light-rail":{"a":"Light Rail","b":"1F688","j":["railway","transportation","vehicle"]},"station":{"a":"Station","b":"1F689","j":["railway","train","transportation","vehicle","public"]},"tram":{"a":"Tram","b":"1F68A","j":["trolleybus","transportation","vehicle"]},"monorail":{"a":"Monorail","b":"1F69D","j":["vehicle","transportation"]},"mountain-railway":{"a":"Mountain Railway","b":"1F69E","j":["car","mountain","railway","transportation","vehicle"]},"tram-car":{"a":"Tram Car","b":"1F68B","j":["car","tram","trolleybus","transportation","vehicle","carriage","public","travel"]},"bus":{"a":"Bus","b":"1F68C","j":["vehicle","car","transportation"]},"oncoming-bus":{"a":"Oncoming Bus","b":"1F68D","j":["bus","oncoming","vehicle","transportation"]},"trolleybus":{"a":"Trolleybus","b":"1F68E","j":["bus","tram","trolley","bart","transportation","vehicle"]},"minibus":{"a":"Minibus","b":"1F690","j":["bus","vehicle","car","transportation"]},"ambulance":{"a":"Ambulance","b":"1F691","j":["vehicle","health","911","hospital"]},"fire-engine":{"a":"Fire Engine","b":"1F692","j":["engine","fire","truck","transportation","cars","vehicle"]},"police-car":{"a":"Police Car","b":"1F693","j":["car","patrol","police","vehicle","cars","transportation","law","legal","enforcement"]},"oncoming-police-car":{"a":"Oncoming Police Car","b":"1F694","j":["car","oncoming","police","vehicle","law","legal","enforcement","911"]},"taxi":{"a":"Taxi","b":"1F695","j":["vehicle","uber","cars","transportation"]},"oncoming-taxi":{"a":"Oncoming Taxi","b":"1F696","j":["oncoming","taxi","vehicle","cars","uber"]},"automobile":{"a":"Automobile","b":"1F697","j":["car","red","transportation","vehicle"]},"oncoming-automobile":{"a":"Oncoming Automobile","b":"1F698","j":["automobile","car","oncoming","vehicle","transportation"]},"sport-utility-vehicle":{"a":"Sport Utility Vehicle","b":"1F699","j":["recreational","sport utility","transportation","vehicle"]},"pickup-truck":{"a":"Pickup Truck","b":"1F6FB","j":["pick-up","pickup","truck","car","transportation"]},"delivery-truck":{"a":"Delivery Truck","b":"1F69A","j":["delivery","truck","cars","transportation"]},"articulated-lorry":{"a":"Articulated Lorry","b":"1F69B","j":["lorry","semi","truck","vehicle","cars","transportation","express"]},"tractor":{"a":"Tractor","b":"1F69C","j":["vehicle","car","farming","agriculture"]},"racing-car":{"a":"Racing Car","b":"1F3CE","j":["car","racing","sports","race","fast","formula","f1"]},"motorcycle":{"a":"Motorcycle","b":"1F3CD","j":["racing","race","sports","fast"]},"motor-scooter":{"a":"Motor Scooter","b":"1F6F5","j":["motor","scooter","vehicle","vespa","sasha"]},"manual-wheelchair":{"a":"Manual Wheelchair","b":"1F9BD","j":["accessibility"]},"motorized-wheelchair":{"a":"Motorized Wheelchair","b":"1F9BC","j":["accessibility"]},"auto-rickshaw":{"a":"Auto Rickshaw","b":"1F6FA","j":["tuk tuk","move","transportation"]},"bicycle":{"a":"Bicycle","b":"1F6B2","j":["bike","sports","exercise","hipster"]},"kick-scooter":{"a":"Kick Scooter","b":"1F6F4","j":["kick","scooter","vehicle","razor"]},"skateboard":{"a":"Skateboard","b":"1F6F9","j":["board"]},"roller-skate":{"a":"Roller Skate","b":"1F6FC","j":["roller","skate","footwear","sports"]},"bus-stop":{"a":"Bus Stop","b":"1F68F","j":["bus","stop","transportation","wait"]},"motorway":{"a":"Motorway","b":"1F6E3","j":["highway","road","cupertino","interstate"]},"railway-track":{"a":"Railway Track","b":"1F6E4","j":["railway","train","transportation"]},"oil-drum":{"a":"Oil Drum","b":"1F6E2","j":["drum","oil","barrell"]},"fuel-pump":{"a":"Fuel Pump","b":"26FD","j":["diesel","fuel","fuelpump","gas","pump","station","gas station","petroleum"]},"wheel":{"a":"Wheel","b":"1F6DE","j":["circle","tire","turn","car","transport"]},"police-car-light":{"a":"Police Car Light","b":"1F6A8","j":["beacon","car","light","police","revolving","ambulance","911","emergency","alert","error","pinged","law","legal"]},"horizontal-traffic-light":{"a":"Horizontal Traffic Light","b":"1F6A5","j":["light","signal","traffic","transportation"]},"vertical-traffic-light":{"a":"Vertical Traffic Light","b":"1F6A6","j":["light","signal","traffic","transportation","driving"]},"stop-sign":{"a":"Stop Sign","b":"1F6D1","j":["octagonal","sign","stop"]},"construction":{"a":"Construction","b":"1F6A7","j":["barrier","wip","progress","caution","warning"]},"anchor":{"a":"Anchor","b":"2693","j":["ship","tool","ferry","sea","boat"]},"ring-buoy":{"a":"Ring Buoy","b":"1F6DF","j":["float","life preserver","life saver","rescue","safety"]},"sailboat":{"a":"Sailboat","b":"26F5","j":["boat","resort","sea","yacht","ship","summer","transportation","water","sailing"]},"canoe":{"a":"Canoe","b":"1F6F6","j":["boat","paddle","water","ship"]},"speedboat":{"a":"Speedboat","b":"1F6A4","j":["boat","ship","transportation","vehicle","summer"]},"passenger-ship":{"a":"Passenger Ship","b":"1F6F3","j":["passenger","ship","yacht","cruise","ferry"]},"ferry":{"a":"Ferry","b":"26F4","j":["boat","passenger","ship","yacht"]},"motor-boat":{"a":"Motor Boat","b":"1F6E5","j":["boat","motorboat","ship"]},"ship":{"a":"Ship","b":"1F6A2","j":["boat","passenger","transportation","titanic","deploy"]},"airplane":{"a":"Airplane","b":"2708","j":["aeroplane","vehicle","transportation","flight","fly"]},"small-airplane":{"a":"Small Airplane","b":"1F6E9","j":["aeroplane","airplane","flight","transportation","fly","vehicle"]},"airplane-departure":{"a":"Airplane Departure","b":"1F6EB","j":["aeroplane","airplane","check-in","departure","departures","airport","flight","landing"]},"airplane-arrival":{"a":"Airplane Arrival","b":"1F6EC","j":["aeroplane","airplane","arrivals","arriving","landing","airport","flight","boarding"]},"parachute":{"a":"Parachute","b":"1FA82","j":["hang-glide","parasail","skydive","fly","glide"]},"seat":{"a":"Seat","b":"1F4BA","j":["chair","sit","airplane","transport","bus","flight","fly"]},"helicopter":{"a":"Helicopter","b":"1F681","j":["vehicle","transportation","fly"]},"suspension-railway":{"a":"Suspension Railway","b":"1F69F","j":["railway","suspension","vehicle","transportation"]},"mountain-cableway":{"a":"Mountain Cableway","b":"1F6A0","j":["cable","gondola","mountain","transportation","vehicle","ski"]},"aerial-tramway":{"a":"Aerial Tramway","b":"1F6A1","j":["aerial","cable","car","gondola","tramway","transportation","vehicle","ski"]},"satellite":{"a":"Satellite","b":"1F6F0","j":["space","communication","gps","orbit","spaceflight","NASA","ISS"]},"rocket":{"a":"Rocket","b":"1F680","j":["space","launch","ship","staffmode","NASA","outer space","outer_space","fly"]},"flying-saucer":{"a":"Flying Saucer","b":"1F6F8","j":["UFO","transportation","vehicle","ufo"]},"bellhop-bell":{"a":"Bellhop Bell","b":"1F6CE","j":["bell","bellhop","hotel","service"]},"luggage":{"a":"Luggage","b":"1F9F3","j":["packing","travel"]},"hourglass-done":{"a":"Hourglass Done","b":"231B","j":["sand","timer","time","clock","oldschool","limit","exam","quiz","test"]},"hourglass-not-done":{"a":"Hourglass Not Done","b":"23F3","j":["hourglass","sand","timer","oldschool","time","countdown"]},"watch":{"a":"Watch","b":"231A","j":["clock","time","accessories"]},"alarm-clock":{"a":"Alarm Clock","b":"23F0","j":["alarm","clock","time","wake"]},"stopwatch":{"a":"Stopwatch","b":"23F1","j":["clock","time","deadline"]},"timer-clock":{"a":"Timer Clock","b":"23F2","j":["clock","timer","alarm"]},"mantelpiece-clock":{"a":"Mantelpiece Clock","b":"1F570","j":["clock","time"]},"twelve-oclock":{"a":"Twelve O’Clock","b":"1F55B","j":["00","12","12:00","clock","o’clock","twelve","twelve_o_clock","time","noon","midnight","midday","late","early","schedule"]},"twelvethirty":{"a":"Twelve-Thirty","b":"1F567","j":["12","12:30","clock","thirty","twelve","twelve-thirty","twelve_thirty","time","late","early","schedule"]},"one-oclock":{"a":"One O’Clock","b":"1F550","j":["00","1","1:00","clock","o’clock","one","one_o_clock","time","late","early","schedule"]},"onethirty":{"a":"One-Thirty","b":"1F55C","j":["1","1:30","clock","one","one-thirty","thirty","one_thirty","time","late","early","schedule"]},"two-oclock":{"a":"Two O’Clock","b":"1F551","j":["00","2","2:00","clock","o’clock","two","two_o_clock","time","late","early","schedule"]},"twothirty":{"a":"Two-Thirty","b":"1F55D","j":["2","2:30","clock","thirty","two","two-thirty","two_thirty","time","late","early","schedule"]},"three-oclock":{"a":"Three O’Clock","b":"1F552","j":["00","3","3:00","clock","o’clock","three","three_o_clock","time","late","early","schedule"]},"threethirty":{"a":"Three-Thirty","b":"1F55E","j":["3","3:30","clock","thirty","three","three-thirty","three_thirty","time","late","early","schedule"]},"four-oclock":{"a":"Four O’Clock","b":"1F553","j":["00","4","4:00","clock","four","o’clock","four_o_clock","time","late","early","schedule"]},"fourthirty":{"a":"Four-Thirty","b":"1F55F","j":["4","4:30","clock","four","four-thirty","thirty","four_thirty","time","late","early","schedule"]},"five-oclock":{"a":"Five O’Clock","b":"1F554","j":["00","5","5:00","clock","five","o’clock","five_o_clock","time","late","early","schedule"]},"fivethirty":{"a":"Five-Thirty","b":"1F560","j":["5","5:30","clock","five","five-thirty","thirty","five_thirty","time","late","early","schedule"]},"six-oclock":{"a":"Six O’Clock","b":"1F555","j":["00","6","6:00","clock","o’clock","six","six_o_clock","time","late","early","schedule","dawn","dusk"]},"sixthirty":{"a":"Six-Thirty","b":"1F561","j":["6","6:30","clock","six","six-thirty","thirty","six_thirty","time","late","early","schedule"]},"seven-oclock":{"a":"Seven O’Clock","b":"1F556","j":["00","7","7:00","clock","o’clock","seven","seven_o_clock","time","late","early","schedule"]},"seventhirty":{"a":"Seven-Thirty","b":"1F562","j":["7","7:30","clock","seven","seven-thirty","thirty","seven_thirty","time","late","early","schedule"]},"eight-oclock":{"a":"Eight O’Clock","b":"1F557","j":["00","8","8:00","clock","eight","o’clock","eight_o_clock","time","late","early","schedule"]},"eightthirty":{"a":"Eight-Thirty","b":"1F563","j":["8","8:30","clock","eight","eight-thirty","thirty","eight_thirty","time","late","early","schedule"]},"nine-oclock":{"a":"Nine O’Clock","b":"1F558","j":["00","9","9:00","clock","nine","o’clock","nine_o_clock","time","late","early","schedule"]},"ninethirty":{"a":"Nine-Thirty","b":"1F564","j":["9","9:30","clock","nine","nine-thirty","thirty","nine_thirty","time","late","early","schedule"]},"ten-oclock":{"a":"Ten O’Clock","b":"1F559","j":["00","10","10:00","clock","o’clock","ten","ten_o_clock","time","late","early","schedule"]},"tenthirty":{"a":"Ten-Thirty","b":"1F565","j":["10","10:30","clock","ten","ten-thirty","thirty","ten_thirty","time","late","early","schedule"]},"eleven-oclock":{"a":"Eleven O’Clock","b":"1F55A","j":["00","11","11:00","clock","eleven","o’clock","eleven_o_clock","time","late","early","schedule"]},"eleventhirty":{"a":"Eleven-Thirty","b":"1F566","j":["11","11:30","clock","eleven","eleven-thirty","thirty","eleven_thirty","time","late","early","schedule"]},"new-moon":{"a":"New Moon","b":"1F311","j":["dark","moon","nature","twilight","planet","space","night","evening","sleep"]},"waxing-crescent-moon":{"a":"Waxing Crescent Moon","b":"1F312","j":["crescent","moon","waxing","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon":{"a":"First Quarter Moon","b":"1F313","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waxing-gibbous-moon":{"a":"Waxing Gibbous Moon","b":"1F314","j":["gibbous","moon","waxing","nature","night","sky","gray","twilight","planet","space","evening","sleep"]},"full-moon":{"a":"Full Moon","b":"1F315","j":["full","moon","nature","yellow","twilight","planet","space","night","evening","sleep"]},"waning-gibbous-moon":{"a":"Waning Gibbous Moon","b":"1F316","j":["gibbous","moon","waning","nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"]},"last-quarter-moon":{"a":"Last Quarter Moon","b":"1F317","j":["moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"waning-crescent-moon":{"a":"Waning Crescent Moon","b":"1F318","j":["crescent","moon","waning","nature","twilight","planet","space","night","evening","sleep"]},"crescent-moon":{"a":"Crescent Moon","b":"1F319","j":["crescent","moon","night","sleep","sky","evening","magic"]},"new-moon-face":{"a":"New Moon Face","b":"1F31A","j":["face","moon","nature","twilight","planet","space","night","evening","sleep"]},"first-quarter-moon-face":{"a":"First Quarter Moon Face","b":"1F31B","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"last-quarter-moon-face":{"a":"Last Quarter Moon Face","b":"1F31C","j":["face","moon","quarter","nature","twilight","planet","space","night","evening","sleep"]},"thermometer":{"a":"Thermometer","b":"1F321","j":["weather","temperature","hot","cold"]},"sun":{"a":"Sun","b":"2600","j":["bright","rays","sunny","weather","nature","brightness","summer","beach","spring"]},"full-moon-face":{"a":"Full Moon Face","b":"1F31D","j":["bright","face","full","moon","nature","twilight","planet","space","night","evening","sleep"]},"sun-with-face":{"a":"Sun with Face","b":"1F31E","j":["bright","face","sun","nature","morning","sky"]},"ringed-planet":{"a":"Ringed Planet","b":"1FA90","j":["saturn","saturnine","outerspace"]},"star":{"a":"Star","b":"2B50","j":["night","yellow"]},"glowing-star":{"a":"Glowing Star","b":"1F31F","j":["glittery","glow","shining","sparkle","star","night","awesome","good","magic"]},"shooting-star":{"a":"Shooting Star","b":"1F320","j":["falling","shooting","star","night","photo"]},"milky-way":{"a":"Milky Way","b":"1F30C","j":["space","photo","stars"]},"cloud":{"a":"Cloud","b":"2601","j":["weather","sky"]},"sun-behind-cloud":{"a":"Sun Behind Cloud","b":"26C5","j":["cloud","sun","weather","nature","cloudy","morning","fall","spring"]},"cloud-with-lightning-and-rain":{"a":"Cloud with Lightning and Rain","b":"26C8","j":["cloud","rain","thunder","weather","lightning"]},"sun-behind-small-cloud":{"a":"Sun Behind Small Cloud","b":"1F324","j":["cloud","sun","weather"]},"sun-behind-large-cloud":{"a":"Sun Behind Large Cloud","b":"1F325","j":["cloud","sun","weather"]},"sun-behind-rain-cloud":{"a":"Sun Behind Rain Cloud","b":"1F326","j":["cloud","rain","sun","weather"]},"cloud-with-rain":{"a":"Cloud with Rain","b":"1F327","j":["cloud","rain","weather"]},"cloud-with-snow":{"a":"Cloud with Snow","b":"1F328","j":["cloud","cold","snow","weather"]},"cloud-with-lightning":{"a":"Cloud with Lightning","b":"1F329","j":["cloud","lightning","weather","thunder"]},"tornado":{"a":"Tornado","b":"1F32A","j":["cloud","whirlwind","weather","cyclone","twister"]},"fog":{"a":"Fog","b":"1F32B","j":["cloud","weather"]},"wind-face":{"a":"Wind Face","b":"1F32C","j":["blow","cloud","face","wind","gust","air"]},"cyclone":{"a":"Cyclone","b":"1F300","j":["dizzy","hurricane","twister","typhoon","weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado"]},"rainbow":{"a":"Rainbow","b":"1F308","j":["rain","nature","happy","unicorn_face","photo","sky","spring"]},"closed-umbrella":{"a":"Closed Umbrella","b":"1F302","j":["clothing","rain","umbrella","weather","drizzle"]},"umbrella":{"a":"Umbrella","b":"2602","j":["clothing","rain","weather","spring"]},"umbrella-with-rain-drops":{"a":"Umbrella with Rain Drops","b":"2614","j":["clothing","drop","rain","umbrella","rainy","weather","spring"]},"umbrella-on-ground":{"a":"Umbrella on Ground","b":"26F1","j":["rain","sun","umbrella","weather","summer"]},"high-voltage":{"a":"High Voltage","b":"26A1","j":["danger","electric","lightning","voltage","zap","thunder","weather","lightning bolt","fast"]},"snowflake":{"a":"Snowflake","b":"2744","j":["cold","snow","winter","season","weather","christmas","xmas"]},"snowman":{"a":"Snowman","b":"2603","j":["cold","snow","winter","season","weather","christmas","xmas","frozen"]},"snowman-without-snow":{"a":"Snowman Without Snow","b":"26C4","j":["cold","snow","snowman","winter","season","weather","christmas","xmas","frozen","without_snow"]},"comet":{"a":"Comet","b":"2604","j":["space"]},"fire":{"a":"Fire","b":"1F525","j":["flame","tool","hot","cook"]},"droplet":{"a":"Droplet","b":"1F4A7","j":["cold","comic","drop","sweat","water","drip","faucet","spring"]},"water-wave":{"a":"Water Wave","b":"1F30A","j":["ocean","water","wave","sea","nature","tsunami","disaster"]},"jackolantern":{"a":"Jack-O-Lantern","b":"1F383","j":["celebration","halloween","jack","jack-o-lantern","lantern","jack_o_lantern","light","pumpkin","creepy","fall"]},"christmas-tree":{"a":"Christmas Tree","b":"1F384","j":["celebration","Christmas","tree","festival","vacation","december","xmas"]},"fireworks":{"a":"Fireworks","b":"1F386","j":["celebration","photo","festival","carnival","congratulations"]},"sparkler":{"a":"Sparkler","b":"1F387","j":["celebration","fireworks","sparkle","stars","night","shine"]},"firecracker":{"a":"Firecracker","b":"1F9E8","j":["dynamite","explosive","fireworks","boom","explode","explosion"]},"sparkles":{"a":"Sparkles","b":"2728","j":["*","sparkle","star","stars","shine","shiny","cool","awesome","good","magic"]},"balloon":{"a":"Balloon","b":"1F388","j":["celebration","party","birthday","circus"]},"party-popper":{"a":"Party Popper","b":"1F389","j":["celebration","party","popper","tada","congratulations","birthday","magic","circus"]},"confetti-ball":{"a":"Confetti Ball","b":"1F38A","j":["ball","celebration","confetti","festival","party","birthday","circus"]},"tanabata-tree":{"a":"Tanabata Tree","b":"1F38B","j":["banner","celebration","Japanese","tree","plant","nature","branch","summer"]},"pine-decoration":{"a":"Pine Decoration","b":"1F38D","j":["bamboo","celebration","Japanese","pine","plant","nature","vegetable","panda"]},"japanese-dolls":{"a":"Japanese Dolls","b":"1F38E","j":["celebration","doll","festival","Japanese","Japanese dolls","japanese","toy","kimono"]},"carp-streamer":{"a":"Carp Streamer","b":"1F38F","j":["carp","celebration","streamer","fish","japanese","koinobori","banner"]},"wind-chime":{"a":"Wind Chime","b":"1F390","j":["bell","celebration","chime","wind","nature","ding","spring"]},"moon-viewing-ceremony":{"a":"Moon Viewing Ceremony","b":"1F391","j":["celebration","ceremony","moon","photo","japan","asia","tsukimi"]},"red-envelope":{"a":"Red Envelope","b":"1F9E7","j":["gift","good luck","hóngbāo","lai see","money"]},"ribbon":{"a":"Ribbon","b":"1F380","j":["celebration","decoration","pink","girl","bowtie"]},"wrapped-gift":{"a":"Wrapped Gift","b":"1F381","j":["box","celebration","gift","present","wrapped","birthday","christmas","xmas"]},"reminder-ribbon":{"a":"Reminder Ribbon","b":"1F397","j":["celebration","reminder","ribbon","sports","cause","support","awareness"]},"admission-tickets":{"a":"Admission Tickets","b":"1F39F","j":["admission","ticket","sports","concert","entrance"]},"ticket":{"a":"Ticket","b":"1F3AB","j":["admission","event","concert","pass"]},"military-medal":{"a":"Military Medal","b":"1F396","j":["celebration","medal","military","award","winning","army"]},"trophy":{"a":"Trophy","b":"1F3C6","j":["prize","win","award","contest","place","ftw","ceremony"]},"sports-medal":{"a":"Sports Medal","b":"1F3C5","j":["medal","award","winning"]},"1st-place-medal":{"a":"1st Place Medal","b":"1F947","j":["first","gold","medal","award","winning"]},"2nd-place-medal":{"a":"2nd Place Medal","b":"1F948","j":["medal","second","silver","award"]},"3rd-place-medal":{"a":"3rd Place Medal","b":"1F949","j":["bronze","medal","third","award"]},"soccer-ball":{"a":"Soccer Ball","b":"26BD","j":["ball","football","soccer","sports"]},"baseball":{"a":"Baseball","b":"26BE","j":["ball","sports","balls"]},"softball":{"a":"Softball","b":"1F94E","j":["ball","glove","underarm","sports","balls"]},"basketball":{"a":"Basketball","b":"1F3C0","j":["ball","hoop","sports","balls","NBA"]},"volleyball":{"a":"Volleyball","b":"1F3D0","j":["ball","game","sports","balls"]},"american-football":{"a":"American Football","b":"1F3C8","j":["american","ball","football","sports","balls","NFL"]},"rugby-football":{"a":"Rugby Football","b":"1F3C9","j":["ball","football","rugby","sports","team"]},"tennis":{"a":"Tennis","b":"1F3BE","j":["ball","racquet","sports","balls","green"]},"flying-disc":{"a":"Flying Disc","b":"1F94F","j":["ultimate","sports","frisbee"]},"bowling":{"a":"Bowling","b":"1F3B3","j":["ball","game","sports","fun","play"]},"cricket-game":{"a":"Cricket Game","b":"1F3CF","j":["ball","bat","game","sports"]},"field-hockey":{"a":"Field Hockey","b":"1F3D1","j":["ball","field","game","hockey","stick","sports"]},"ice-hockey":{"a":"Ice Hockey","b":"1F3D2","j":["game","hockey","ice","puck","stick","sports"]},"lacrosse":{"a":"Lacrosse","b":"1F94D","j":["ball","goal","stick","sports"]},"ping-pong":{"a":"Ping Pong","b":"1F3D3","j":["ball","bat","game","paddle","table tennis","sports","pingpong"]},"badminton":{"a":"Badminton","b":"1F3F8","j":["birdie","game","racquet","shuttlecock","sports"]},"boxing-glove":{"a":"Boxing Glove","b":"1F94A","j":["boxing","glove","sports","fighting"]},"martial-arts-uniform":{"a":"Martial Arts Uniform","b":"1F94B","j":["judo","karate","martial arts","taekwondo","uniform"]},"goal-net":{"a":"Goal Net","b":"1F945","j":["goal","net","sports"]},"flag-in-hole":{"a":"Flag in Hole","b":"26F3","j":["golf","hole","sports","business","flag","summer"]},"ice-skate":{"a":"Ice Skate","b":"26F8","j":["ice","skate","sports"]},"fishing-pole":{"a":"Fishing Pole","b":"1F3A3","j":["fish","pole","food","hobby","summer"]},"diving-mask":{"a":"Diving Mask","b":"1F93F","j":["diving","scuba","snorkeling","sport","ocean"]},"running-shirt":{"a":"Running Shirt","b":"1F3BD","j":["athletics","running","sash","shirt","play","pageant"]},"skis":{"a":"Skis","b":"1F3BF","j":["ski","snow","sports","winter","cold"]},"sled":{"a":"Sled","b":"1F6F7","j":["sledge","sleigh","luge","toboggan"]},"curling-stone":{"a":"Curling Stone","b":"1F94C","j":["game","rock","sports"]},"bullseye":{"a":"Bullseye","b":"1F3AF","j":["dart","direct hit","game","hit","target","direct_hit","play","bar"]},"yoyo":{"a":"Yo-Yo","b":"1FA80","j":["fluctuate","toy","yo-yo","yo_yo"]},"kite":{"a":"Kite","b":"1FA81","j":["fly","soar","wind"]},"water-pistol":{"a":"Water Pistol","b":"1F52B","j":["gun","handgun","pistol","revolver","tool","water","weapon","violence"]},"pool-8-ball":{"a":"Pool 8 Ball","b":"1F3B1","j":["8","ball","billiard","eight","game","pool","hobby","luck","magic"]},"crystal-ball":{"a":"Crystal Ball","b":"1F52E","j":["ball","crystal","fairy tale","fantasy","fortune","tool","disco","party","magic","circus","fortune_teller"]},"magic-wand":{"a":"Magic Wand","b":"1FA84","j":["magic","witch","wizard","supernature","power"]},"video-game":{"a":"Video Game","b":"1F3AE","j":["controller","game","play","console","PS4"]},"joystick":{"a":"Joystick","b":"1F579","j":["game","video game","play"]},"slot-machine":{"a":"Slot Machine","b":"1F3B0","j":["game","slot","bet","gamble","vegas","fruit machine","luck","casino"]},"game-die":{"a":"Game Die","b":"1F3B2","j":["dice","die","game","random","tabletop","play","luck"]},"puzzle-piece":{"a":"Puzzle Piece","b":"1F9E9","j":["clue","interlocking","jigsaw","piece","puzzle"]},"teddy-bear":{"a":"Teddy Bear","b":"1F9F8","j":["plaything","plush","stuffed","toy"]},"piata":{"a":"Piñata","b":"1FA85","j":["celebration","party","piñata","pinata","mexico","candy"]},"mirror-ball":{"a":"Mirror Ball","b":"1FAA9","j":["dance","disco","glitter","party"]},"nesting-dolls":{"a":"Nesting Dolls","b":"1FA86","j":["doll","nesting","russia","matryoshka","toy"]},"spade-suit":{"a":"Spade Suit","b":"2660","j":["card","game","poker","cards","suits","magic"]},"heart-suit":{"a":"Heart Suit","b":"2665","j":["card","game","poker","cards","magic","suits"]},"diamond-suit":{"a":"Diamond Suit","b":"2666","j":["card","game","poker","cards","magic","suits"]},"club-suit":{"a":"Club Suit","b":"2663","j":["card","game","poker","cards","magic","suits"]},"chess-pawn":{"a":"Chess Pawn","b":"265F","j":["chess","dupe","expendable"]},"joker":{"a":"Joker","b":"1F0CF","j":["card","game","wildcard","poker","cards","play","magic"]},"mahjong-red-dragon":{"a":"Mahjong Red Dragon","b":"1F004","j":["game","mahjong","red","play","chinese","kanji"]},"flower-playing-cards":{"a":"Flower Playing Cards","b":"1F3B4","j":["card","flower","game","Japanese","playing","sunset","red"]},"performing-arts":{"a":"Performing Arts","b":"1F3AD","j":["art","mask","performing","theater","theatre","acting","drama"]},"framed-picture":{"a":"Framed Picture","b":"1F5BC","j":["art","frame","museum","painting","picture","photography"]},"artist-palette":{"a":"Artist Palette","b":"1F3A8","j":["art","museum","painting","palette","design","paint","draw","colors"]},"thread":{"a":"Thread","b":"1F9F5","j":["needle","sewing","spool","string"]},"sewing-needle":{"a":"Sewing Needle","b":"1FAA1","j":["embroidery","needle","sewing","stitches","sutures","tailoring"]},"yarn":{"a":"Yarn","b":"1F9F6","j":["ball","crochet","knit"]},"knot":{"a":"Knot","b":"1FAA2","j":["rope","tangled","tie","twine","twist","scout"]},"glasses":{"a":"Glasses","b":"1F453","j":["clothing","eye","eyeglasses","eyewear","fashion","accessories","eyesight","nerdy","dork","geek"]},"sunglasses":{"a":"Sunglasses","b":"1F576","j":["dark","eye","eyewear","glasses","face","cool","accessories"]},"goggles":{"a":"Goggles","b":"1F97D","j":["eye protection","swimming","welding","eyes","protection","safety"]},"lab-coat":{"a":"Lab Coat","b":"1F97C","j":["doctor","experiment","scientist","chemist"]},"safety-vest":{"a":"Safety Vest","b":"1F9BA","j":["emergency","safety","vest","protection"]},"necktie":{"a":"Necktie","b":"1F454","j":["clothing","tie","shirt","suitup","formal","fashion","cloth","business"]},"tshirt":{"a":"T-Shirt","b":"1F455","j":["clothing","shirt","t-shirt","t_shirt","fashion","cloth","casual","tee"]},"jeans":{"a":"Jeans","b":"1F456","j":["clothing","pants","trousers","fashion","shopping"]},"scarf":{"a":"Scarf","b":"1F9E3","j":["neck","winter","clothes"]},"gloves":{"a":"Gloves","b":"1F9E4","j":["hand","hands","winter","clothes"]},"coat":{"a":"Coat","b":"1F9E5","j":["jacket"]},"socks":{"a":"Socks","b":"1F9E6","j":["stocking","stockings","clothes"]},"dress":{"a":"Dress","b":"1F457","j":["clothing","clothes","fashion","shopping"]},"kimono":{"a":"Kimono","b":"1F458","j":["clothing","dress","fashion","women","female","japanese"]},"sari":{"a":"Sari","b":"1F97B","j":["clothing","dress"]},"onepiece-swimsuit":{"a":"One-Piece Swimsuit","b":"1FA71","j":["bathing suit","one-piece swimsuit","one_piece_swimsuit","fashion"]},"briefs":{"a":"Briefs","b":"1FA72","j":["bathing suit","one-piece","swimsuit","underwear","clothing"]},"shorts":{"a":"Shorts","b":"1FA73","j":["bathing suit","pants","underwear","clothing"]},"bikini":{"a":"Bikini","b":"1F459","j":["clothing","swim","swimming","female","woman","girl","fashion","beach","summer"]},"womans-clothes":{"a":"Woman’S Clothes","b":"1F45A","j":["clothing","woman","woman’s clothes","woman_s_clothes","fashion","shopping_bags","female"]},"folding-hand-fan":{"a":"⊛ Folding Hand Fan","b":"1FAAD","j":["cooling","dance","fan","flutter","folding hand fan","hot","shy"]},"purse":{"a":"Purse","b":"1F45B","j":["clothing","coin","fashion","accessories","money","sales","shopping"]},"handbag":{"a":"Handbag","b":"1F45C","j":["bag","clothing","purse","fashion","accessory","accessories","shopping"]},"clutch-bag":{"a":"Clutch Bag","b":"1F45D","j":["bag","clothing","pouch","accessories","shopping"]},"shopping-bags":{"a":"Shopping Bags","b":"1F6CD","j":["bag","hotel","shopping","mall","buy","purchase"]},"backpack":{"a":"Backpack","b":"1F392","j":["bag","rucksack","satchel","school","student","education"]},"thong-sandal":{"a":"Thong Sandal","b":"1FA74","j":["beach sandals","sandals","thong sandals","thongs","zōri","footwear","summer"]},"mans-shoe":{"a":"Man’S Shoe","b":"1F45E","j":["clothing","man","man’s shoe","shoe","man_s_shoe","fashion","male"]},"running-shoe":{"a":"Running Shoe","b":"1F45F","j":["athletic","clothing","shoe","sneaker","shoes","sports","sneakers"]},"hiking-boot":{"a":"Hiking Boot","b":"1F97E","j":["backpacking","boot","camping","hiking"]},"flat-shoe":{"a":"Flat Shoe","b":"1F97F","j":["ballet flat","slip-on","slipper","ballet"]},"highheeled-shoe":{"a":"High-Heeled Shoe","b":"1F460","j":["clothing","heel","high-heeled shoe","shoe","woman","high_heeled_shoe","fashion","shoes","female","pumps","stiletto"]},"womans-sandal":{"a":"Woman’S Sandal","b":"1F461","j":["clothing","sandal","shoe","woman","woman’s sandal","woman_s_sandal","shoes","fashion","flip flops"]},"ballet-shoes":{"a":"Ballet Shoes","b":"1FA70","j":["ballet","dance"]},"womans-boot":{"a":"Woman’S Boot","b":"1F462","j":["boot","clothing","shoe","woman","woman’s boot","woman_s_boot","shoes","fashion"]},"hair-pick":{"a":"⊛ Hair Pick","b":"1FAAE","j":["Afro","comb","hair","pick"]},"crown":{"a":"Crown","b":"1F451","j":["clothing","king","queen","kod","leader","royalty","lord"]},"womans-hat":{"a":"Woman’S Hat","b":"1F452","j":["clothing","hat","woman","woman’s hat","woman_s_hat","fashion","accessories","female","lady","spring"]},"top-hat":{"a":"Top Hat","b":"1F3A9","j":["clothing","hat","top","tophat","magic","gentleman","classy","circus"]},"graduation-cap":{"a":"Graduation Cap","b":"1F393","j":["cap","celebration","clothing","graduation","hat","school","college","degree","university","legal","learn","education"]},"billed-cap":{"a":"Billed Cap","b":"1F9E2","j":["baseball cap","cap","baseball"]},"military-helmet":{"a":"Military Helmet","b":"1FA96","j":["army","helmet","military","soldier","warrior","protection"]},"rescue-workers-helmet":{"a":"Rescue Worker’S Helmet","b":"26D1","j":["aid","cross","face","hat","helmet","rescue worker’s helmet","rescue_worker_s_helmet","construction","build"]},"prayer-beads":{"a":"Prayer Beads","b":"1F4FF","j":["beads","clothing","necklace","prayer","religion","dhikr","religious"]},"lipstick":{"a":"Lipstick","b":"1F484","j":["cosmetics","makeup","female","girl","fashion","woman"]},"ring":{"a":"Ring","b":"1F48D","j":["diamond","wedding","propose","marriage","valentines","fashion","jewelry","gem","engagement"]},"gem-stone":{"a":"Gem Stone","b":"1F48E","j":["diamond","gem","jewel","blue","ruby","jewelry"]},"muted-speaker":{"a":"Muted Speaker","b":"1F507","j":["mute","quiet","silent","speaker","sound","volume","silence"]},"speaker-low-volume":{"a":"Speaker Low Volume","b":"1F508","j":["soft","sound","volume","silence","broadcast"]},"speaker-medium-volume":{"a":"Speaker Medium Volume","b":"1F509","j":["medium","volume","speaker","broadcast"]},"speaker-high-volume":{"a":"Speaker High Volume","b":"1F50A","j":["loud","volume","noise","noisy","speaker","broadcast"]},"loudspeaker":{"a":"Loudspeaker","b":"1F4E2","j":["loud","public address","volume","sound"]},"megaphone":{"a":"Megaphone","b":"1F4E3","j":["cheering","sound","speaker","volume"]},"postal-horn":{"a":"Postal Horn","b":"1F4EF","j":["horn","post","postal","instrument","music"]},"bell":{"a":"Bell","b":"1F514","j":["sound","notification","christmas","xmas","chime"]},"bell-with-slash":{"a":"Bell with Slash","b":"1F515","j":["bell","forbidden","mute","quiet","silent","sound","volume"]},"musical-score":{"a":"Musical Score","b":"1F3BC","j":["music","score","treble","clef","compose"]},"musical-note":{"a":"Musical Note","b":"1F3B5","j":["music","note","score","tone","sound"]},"musical-notes":{"a":"Musical Notes","b":"1F3B6","j":["music","note","notes","score"]},"studio-microphone":{"a":"Studio Microphone","b":"1F399","j":["mic","microphone","music","studio","sing","recording","artist","talkshow"]},"level-slider":{"a":"Level Slider","b":"1F39A","j":["level","music","slider","scale"]},"control-knobs":{"a":"Control Knobs","b":"1F39B","j":["control","knobs","music","dial"]},"microphone":{"a":"Microphone","b":"1F3A4","j":["karaoke","mic","sound","music","PA","sing","talkshow"]},"headphone":{"a":"Headphone","b":"1F3A7","j":["earbud","music","score","gadgets"]},"radio":{"a":"Radio","b":"1F4FB","j":["video","communication","music","podcast","program"]},"saxophone":{"a":"Saxophone","b":"1F3B7","j":["instrument","music","sax","jazz","blues"]},"accordion":{"a":"Accordion","b":"1FA97","j":["concertina","squeeze box","music"]},"guitar":{"a":"Guitar","b":"1F3B8","j":["instrument","music"]},"musical-keyboard":{"a":"Musical Keyboard","b":"1F3B9","j":["instrument","keyboard","music","piano","compose"]},"trumpet":{"a":"Trumpet","b":"1F3BA","j":["instrument","music","brass"]},"violin":{"a":"Violin","b":"1F3BB","j":["instrument","music","orchestra","symphony"]},"banjo":{"a":"Banjo","b":"1FA95","j":["music","stringed","instructment"]},"drum":{"a":"Drum","b":"1F941","j":["drumsticks","music","instrument","snare"]},"long-drum":{"a":"Long Drum","b":"1FA98","j":["beat","conga","drum","rhythm","music"]},"maracas":{"a":"⊛ Maracas","b":"1FA87","j":["instrument","maracas","music","percussion","rattle","shake"]},"flute":{"a":"⊛ Flute","b":"1FA88","j":["fife","flute","music","pipe","recorder","woodwind"]},"mobile-phone":{"a":"Mobile Phone","b":"1F4F1","j":["cell","mobile","phone","telephone","technology","apple","gadgets","dial"]},"mobile-phone-with-arrow":{"a":"Mobile Phone with Arrow","b":"1F4F2","j":["arrow","cell","mobile","phone","receive","iphone","incoming"]},"telephone":{"a":"Telephone","b":"260E","j":["phone","technology","communication","dial"]},"telephone-receiver":{"a":"Telephone Receiver","b":"1F4DE","j":["phone","receiver","telephone","technology","communication","dial"]},"pager":{"a":"Pager","b":"1F4DF","j":["bbcall","oldschool","90s"]},"fax-machine":{"a":"Fax Machine","b":"1F4E0","j":["fax","communication","technology"]},"battery":{"a":"Battery","b":"1F50B","j":["power","energy","sustain"]},"low-battery":{"a":"Low Battery","b":"1FAAB","j":["electronic","low energy","drained","dead"]},"electric-plug":{"a":"Electric Plug","b":"1F50C","j":["electric","electricity","plug","charger","power"]},"laptop":{"a":"Laptop","b":"1F4BB","j":["computer","pc","personal","technology","screen","display","monitor"]},"desktop-computer":{"a":"Desktop Computer","b":"1F5A5","j":["computer","desktop","technology","computing","screen"]},"printer":{"a":"Printer","b":"1F5A8","j":["computer","paper","ink"]},"keyboard":{"a":"Keyboard","b":"2328","j":["computer","technology","type","input","text"]},"computer-mouse":{"a":"Computer Mouse","b":"1F5B1","j":["computer","click"]},"trackball":{"a":"Trackball","b":"1F5B2","j":["computer","technology","trackpad"]},"computer-disk":{"a":"Computer Disk","b":"1F4BD","j":["computer","disk","minidisk","optical","technology","record","data","90s"]},"floppy-disk":{"a":"Floppy Disk","b":"1F4BE","j":["computer","disk","floppy","oldschool","technology","save","90s","80s"]},"optical-disk":{"a":"Optical Disk","b":"1F4BF","j":["CD","computer","disk","optical","technology","dvd","disc","90s"]},"dvd":{"a":"Dvd","b":"1F4C0","j":["Blu-ray","computer","disk","DVD","optical","cd","disc"]},"abacus":{"a":"Abacus","b":"1F9EE","j":["calculation"]},"movie-camera":{"a":"Movie Camera","b":"1F3A5","j":["camera","cinema","movie","film","record"]},"film-frames":{"a":"Film Frames","b":"1F39E","j":["cinema","film","frames","movie"]},"film-projector":{"a":"Film Projector","b":"1F4FD","j":["cinema","film","movie","projector","video","tape","record"]},"clapper-board":{"a":"Clapper Board","b":"1F3AC","j":["clapper","movie","film","record"]},"television":{"a":"Television","b":"1F4FA","j":["tv","video","technology","program","oldschool","show"]},"camera":{"a":"Camera","b":"1F4F7","j":["video","gadgets","photography"]},"camera-with-flash":{"a":"Camera with Flash","b":"1F4F8","j":["camera","flash","video","photography","gadgets"]},"video-camera":{"a":"Video Camera","b":"1F4F9","j":["camera","video","film","record"]},"videocassette":{"a":"Videocassette","b":"1F4FC","j":["tape","vhs","video","record","oldschool","90s","80s"]},"magnifying-glass-tilted-left":{"a":"Magnifying Glass Tilted Left","b":"1F50D","j":["glass","magnifying","search","tool","zoom","find","detective"]},"magnifying-glass-tilted-right":{"a":"Magnifying Glass Tilted Right","b":"1F50E","j":["glass","magnifying","search","tool","zoom","find","detective"]},"candle":{"a":"Candle","b":"1F56F","j":["light","fire","wax"]},"light-bulb":{"a":"Light Bulb","b":"1F4A1","j":["bulb","comic","electric","idea","light","electricity"]},"flashlight":{"a":"Flashlight","b":"1F526","j":["electric","light","tool","torch","dark","camping","sight","night"]},"red-paper-lantern":{"a":"Red Paper Lantern","b":"1F3EE","j":["bar","lantern","light","red","paper","halloween","spooky"]},"diya-lamp":{"a":"Diya Lamp","b":"1FA94","j":["diya","lamp","oil","lighting"]},"notebook-with-decorative-cover":{"a":"Notebook with Decorative Cover","b":"1F4D4","j":["book","cover","decorated","notebook","classroom","notes","record","paper","study"]},"closed-book":{"a":"Closed Book","b":"1F4D5","j":["book","closed","read","library","knowledge","textbook","learn"]},"open-book":{"a":"Open Book","b":"1F4D6","j":["book","open","read","library","knowledge","literature","learn","study"]},"green-book":{"a":"Green Book","b":"1F4D7","j":["book","green","read","library","knowledge","study"]},"blue-book":{"a":"Blue Book","b":"1F4D8","j":["blue","book","read","library","knowledge","learn","study"]},"orange-book":{"a":"Orange Book","b":"1F4D9","j":["book","orange","read","library","knowledge","textbook","study"]},"books":{"a":"Books","b":"1F4DA","j":["book","literature","library","study"]},"notebook":{"a":"Notebook","b":"1F4D3","j":["stationery","record","notes","paper","study"]},"ledger":{"a":"Ledger","b":"1F4D2","j":["notebook","notes","paper"]},"page-with-curl":{"a":"Page with Curl","b":"1F4C3","j":["curl","document","page","documents","office","paper"]},"scroll":{"a":"Scroll","b":"1F4DC","j":["paper","documents","ancient","history"]},"page-facing-up":{"a":"Page Facing Up","b":"1F4C4","j":["document","page","documents","office","paper","information"]},"newspaper":{"a":"Newspaper","b":"1F4F0","j":["news","paper","press","headline"]},"rolledup-newspaper":{"a":"Rolled-Up Newspaper","b":"1F5DE","j":["news","newspaper","paper","rolled","rolled-up newspaper","rolled_up_newspaper","press","headline"]},"bookmark-tabs":{"a":"Bookmark Tabs","b":"1F4D1","j":["bookmark","mark","marker","tabs","favorite","save","order","tidy"]},"bookmark":{"a":"Bookmark","b":"1F516","j":["mark","favorite","label","save"]},"label":{"a":"Label","b":"1F3F7","j":["sale","tag"]},"money-bag":{"a":"Money Bag","b":"1F4B0","j":["bag","dollar","money","moneybag","payment","coins","sale"]},"coin":{"a":"Coin","b":"1FA99","j":["gold","metal","money","silver","treasure","currency"]},"yen-banknote":{"a":"Yen Banknote","b":"1F4B4","j":["banknote","bill","currency","money","note","yen","sales","japanese","dollar"]},"dollar-banknote":{"a":"Dollar Banknote","b":"1F4B5","j":["banknote","bill","currency","dollar","money","note","sales"]},"euro-banknote":{"a":"Euro Banknote","b":"1F4B6","j":["banknote","bill","currency","euro","money","note","sales","dollar"]},"pound-banknote":{"a":"Pound Banknote","b":"1F4B7","j":["banknote","bill","currency","money","note","pound","british","sterling","sales","bills","uk","england"]},"money-with-wings":{"a":"Money with Wings","b":"1F4B8","j":["banknote","bill","fly","money","wings","dollar","bills","payment","sale"]},"credit-card":{"a":"Credit Card","b":"1F4B3","j":["card","credit","money","sales","dollar","bill","payment","shopping"]},"receipt":{"a":"Receipt","b":"1F9FE","j":["accounting","bookkeeping","evidence","proof","expenses"]},"chart-increasing-with-yen":{"a":"Chart Increasing with Yen","b":"1F4B9","j":["chart","graph","growth","money","yen","green-square","presentation","stats"]},"envelope":{"a":"Envelope","b":"2709","j":["email","letter","postal","inbox","communication"]},"email":{"a":"E-Mail","b":"1F4E7","j":["e-mail","letter","mail","e_mail","communication","inbox"]},"incoming-envelope":{"a":"Incoming Envelope","b":"1F4E8","j":["e-mail","email","envelope","incoming","letter","receive","inbox"]},"envelope-with-arrow":{"a":"Envelope with Arrow","b":"1F4E9","j":["arrow","e-mail","email","envelope","outgoing","communication"]},"outbox-tray":{"a":"Outbox Tray","b":"1F4E4","j":["box","letter","mail","outbox","sent","tray","inbox","email"]},"inbox-tray":{"a":"Inbox Tray","b":"1F4E5","j":["box","inbox","letter","mail","receive","tray","email","documents"]},"package":{"a":"Package","b":"1F4E6","j":["box","parcel","mail","gift","cardboard","moving"]},"closed-mailbox-with-raised-flag":{"a":"Closed Mailbox with Raised Flag","b":"1F4EB","j":["closed","mail","mailbox","postbox","email","inbox","communication"]},"closed-mailbox-with-lowered-flag":{"a":"Closed Mailbox with Lowered Flag","b":"1F4EA","j":["closed","lowered","mail","mailbox","postbox","email","communication","inbox"]},"open-mailbox-with-raised-flag":{"a":"Open Mailbox with Raised Flag","b":"1F4EC","j":["mail","mailbox","open","postbox","email","inbox","communication"]},"open-mailbox-with-lowered-flag":{"a":"Open Mailbox with Lowered Flag","b":"1F4ED","j":["lowered","mail","mailbox","open","postbox","email","inbox"]},"postbox":{"a":"Postbox","b":"1F4EE","j":["mail","mailbox","email","letter","envelope"]},"ballot-box-with-ballot":{"a":"Ballot Box with Ballot","b":"1F5F3","j":["ballot","box","election","vote"]},"pencil":{"a":"Pencil","b":"270F","j":["stationery","write","paper","writing","school","study"]},"black-nib":{"a":"Black Nib","b":"2712","j":["nib","pen","stationery","writing","write"]},"fountain-pen":{"a":"Fountain Pen","b":"1F58B","j":["fountain","pen","stationery","writing","write"]},"pen":{"a":"Pen","b":"1F58A","j":["ballpoint","stationery","writing","write"]},"paintbrush":{"a":"Paintbrush","b":"1F58C","j":["painting","drawing","creativity","art"]},"crayon":{"a":"Crayon","b":"1F58D","j":["drawing","creativity"]},"memo":{"a":"Memo","b":"1F4DD","j":["pencil","write","documents","stationery","paper","writing","legal","exam","quiz","test","study","compose"]},"briefcase":{"a":"Briefcase","b":"1F4BC","j":["business","documents","work","law","legal","job","career"]},"file-folder":{"a":"File Folder","b":"1F4C1","j":["file","folder","documents","business","office"]},"open-file-folder":{"a":"Open File Folder","b":"1F4C2","j":["file","folder","open","documents","load"]},"card-index-dividers":{"a":"Card Index Dividers","b":"1F5C2","j":["card","dividers","index","organizing","business","stationery"]},"calendar":{"a":"Calendar","b":"1F4C5","j":["date","schedule"]},"tearoff-calendar":{"a":"Tear-off Calendar","b":"1F4C6","j":["calendar","tear-off calendar","tear_off_calendar","schedule","date","planning"]},"spiral-notepad":{"a":"Spiral Notepad","b":"1F5D2","j":["note","pad","spiral","memo","stationery"]},"spiral-calendar":{"a":"Spiral Calendar","b":"1F5D3","j":["calendar","pad","spiral","date","schedule","planning"]},"card-index":{"a":"Card Index","b":"1F4C7","j":["card","index","rolodex","business","stationery"]},"chart-increasing":{"a":"Chart Increasing","b":"1F4C8","j":["chart","graph","growth","trend","upward","presentation","stats","recovery","business","economics","money","sales","good","success"]},"chart-decreasing":{"a":"Chart Decreasing","b":"1F4C9","j":["chart","down","graph","trend","presentation","stats","recession","business","economics","money","sales","bad","failure"]},"bar-chart":{"a":"Bar Chart","b":"1F4CA","j":["bar","chart","graph","presentation","stats"]},"clipboard":{"a":"Clipboard","b":"1F4CB","j":["stationery","documents"]},"pushpin":{"a":"Pushpin","b":"1F4CC","j":["pin","stationery","mark","here"]},"round-pushpin":{"a":"Round Pushpin","b":"1F4CD","j":["pin","pushpin","stationery","location","map","here"]},"paperclip":{"a":"Paperclip","b":"1F4CE","j":["documents","stationery"]},"linked-paperclips":{"a":"Linked Paperclips","b":"1F587","j":["link","paperclip","documents","stationery"]},"straight-ruler":{"a":"Straight Ruler","b":"1F4CF","j":["ruler","straight edge","stationery","calculate","length","math","school","drawing","architect","sketch"]},"triangular-ruler":{"a":"Triangular Ruler","b":"1F4D0","j":["ruler","set","triangle","stationery","math","architect","sketch"]},"scissors":{"a":"Scissors","b":"2702","j":["cutting","tool","stationery","cut"]},"card-file-box":{"a":"Card File Box","b":"1F5C3","j":["box","card","file","business","stationery"]},"file-cabinet":{"a":"File Cabinet","b":"1F5C4","j":["cabinet","file","filing","organizing"]},"wastebasket":{"a":"Wastebasket","b":"1F5D1","j":["bin","trash","rubbish","garbage","toss"]},"locked":{"a":"Locked","b":"1F512","j":["closed","security","password","padlock"]},"unlocked":{"a":"Unlocked","b":"1F513","j":["lock","open","unlock","privacy","security"]},"locked-with-pen":{"a":"Locked with Pen","b":"1F50F","j":["ink","lock","nib","pen","privacy","security","secret"]},"locked-with-key":{"a":"Locked with Key","b":"1F510","j":["closed","key","lock","secure","security","privacy"]},"key":{"a":"Key","b":"1F511","j":["lock","password","door"]},"old-key":{"a":"Old Key","b":"1F5DD","j":["clue","key","lock","old","door","password"]},"hammer":{"a":"Hammer","b":"1F528","j":["tool","tools","build","create"]},"axe":{"a":"Axe","b":"1FA93","j":["chop","hatchet","split","wood","tool","cut"]},"pick":{"a":"Pick","b":"26CF","j":["mining","tool","tools","dig"]},"hammer-and-pick":{"a":"Hammer and Pick","b":"2692","j":["hammer","pick","tool","tools","build","create"]},"hammer-and-wrench":{"a":"Hammer and Wrench","b":"1F6E0","j":["hammer","spanner","tool","wrench","tools","build","create"]},"dagger":{"a":"Dagger","b":"1F5E1","j":["knife","weapon"]},"crossed-swords":{"a":"Crossed Swords","b":"2694","j":["crossed","swords","weapon"]},"bomb":{"a":"Bomb","b":"1F4A3","j":["comic","boom","explode","explosion","terrorism"]},"boomerang":{"a":"Boomerang","b":"1FA83","j":["australia","rebound","repercussion","weapon"]},"bow-and-arrow":{"a":"Bow and Arrow","b":"1F3F9","j":["archer","arrow","bow","Sagittarius","zodiac","sports"]},"shield":{"a":"Shield","b":"1F6E1","j":["weapon","protection","security"]},"carpentry-saw":{"a":"Carpentry Saw","b":"1FA9A","j":["carpenter","lumber","saw","tool","cut","chop"]},"wrench":{"a":"Wrench","b":"1F527","j":["spanner","tool","tools","diy","ikea","fix","maintainer"]},"screwdriver":{"a":"Screwdriver","b":"1FA9B","j":["screw","tool","tools"]},"nut-and-bolt":{"a":"Nut and Bolt","b":"1F529","j":["bolt","nut","tool","handy","tools","fix"]},"gear":{"a":"Gear","b":"2699","j":["cog","cogwheel","tool"]},"clamp":{"a":"Clamp","b":"1F5DC","j":["compress","tool","vice"]},"balance-scale":{"a":"Balance Scale","b":"2696","j":["balance","justice","Libra","scale","zodiac","law","fairness","weight"]},"white-cane":{"a":"White Cane","b":"1F9AF","j":["accessibility","blind","probing_cane"]},"link":{"a":"Link","b":"1F517","j":["rings","url"]},"chains":{"a":"Chains","b":"26D3","j":["chain","lock","arrest"]},"hook":{"a":"Hook","b":"1FA9D","j":["catch","crook","curve","ensnare","selling point","tools"]},"toolbox":{"a":"Toolbox","b":"1F9F0","j":["chest","mechanic","tool","tools","diy","fix","maintainer"]},"magnet":{"a":"Magnet","b":"1F9F2","j":["attraction","horseshoe","magnetic"]},"ladder":{"a":"Ladder","b":"1FA9C","j":["climb","rung","step","tools"]},"alembic":{"a":"Alembic","b":"2697","j":["chemistry","tool","distilling","science","experiment"]},"test-tube":{"a":"Test Tube","b":"1F9EA","j":["chemist","chemistry","experiment","lab","science"]},"petri-dish":{"a":"Petri Dish","b":"1F9EB","j":["bacteria","biologist","biology","culture","lab"]},"dna":{"a":"Dna","b":"1F9EC","j":["biologist","evolution","gene","genetics","life"]},"microscope":{"a":"Microscope","b":"1F52C","j":["science","tool","laboratory","experiment","zoomin","study"]},"telescope":{"a":"Telescope","b":"1F52D","j":["science","tool","stars","space","zoom","astronomy"]},"satellite-antenna":{"a":"Satellite Antenna","b":"1F4E1","j":["antenna","dish","satellite","communication","future","radio","space"]},"syringe":{"a":"Syringe","b":"1F489","j":["medicine","needle","shot","sick","health","hospital","drugs","blood","doctor","nurse"]},"drop-of-blood":{"a":"Drop of Blood","b":"1FA78","j":["bleed","blood donation","injury","medicine","menstruation","period","hurt","harm","wound"]},"pill":{"a":"Pill","b":"1F48A","j":["doctor","medicine","sick","health","pharmacy","drug"]},"adhesive-bandage":{"a":"Adhesive Bandage","b":"1FA79","j":["bandage","heal"]},"crutch":{"a":"Crutch","b":"1FA7C","j":["cane","disability","hurt","mobility aid","stick","accessibility","assist"]},"stethoscope":{"a":"Stethoscope","b":"1FA7A","j":["doctor","heart","medicine","health"]},"xray":{"a":"X-Ray","b":"1FA7B","j":["bones","doctor","medical","skeleton","x-ray","medicine"]},"door":{"a":"Door","b":"1F6AA","j":["house","entry","exit"]},"elevator":{"a":"Elevator","b":"1F6D7","j":["accessibility","hoist","lift"]},"mirror":{"a":"Mirror","b":"1FA9E","j":["reflection","reflector","speculum"]},"window":{"a":"Window","b":"1FA9F","j":["frame","fresh air","opening","transparent","view","scenery"]},"bed":{"a":"Bed","b":"1F6CF","j":["hotel","sleep","rest"]},"couch-and-lamp":{"a":"Couch and Lamp","b":"1F6CB","j":["couch","hotel","lamp","read","chill"]},"chair":{"a":"Chair","b":"1FA91","j":["seat","sit","furniture"]},"toilet":{"a":"Toilet","b":"1F6BD","j":["restroom","wc","washroom","bathroom","potty"]},"plunger":{"a":"Plunger","b":"1FAA0","j":["force cup","plumber","suction","toilet"]},"shower":{"a":"Shower","b":"1F6BF","j":["water","clean","bathroom"]},"bathtub":{"a":"Bathtub","b":"1F6C1","j":["bath","clean","shower","bathroom"]},"mouse-trap":{"a":"Mouse Trap","b":"1FAA4","j":["bait","mousetrap","snare","trap","cheese"]},"razor":{"a":"Razor","b":"1FA92","j":["sharp","shave","cut"]},"lotion-bottle":{"a":"Lotion Bottle","b":"1F9F4","j":["lotion","moisturizer","shampoo","sunscreen"]},"safety-pin":{"a":"Safety Pin","b":"1F9F7","j":["diaper","punk rock"]},"broom":{"a":"Broom","b":"1F9F9","j":["cleaning","sweeping","witch"]},"basket":{"a":"Basket","b":"1F9FA","j":["farming","laundry","picnic"]},"roll-of-paper":{"a":"Roll of Paper","b":"1F9FB","j":["paper towels","toilet paper","roll"]},"bucket":{"a":"Bucket","b":"1FAA3","j":["cask","pail","vat","water","container"]},"soap":{"a":"Soap","b":"1F9FC","j":["bar","bathing","cleaning","lather","soapdish"]},"bubbles":{"a":"Bubbles","b":"1FAE7","j":["burp","clean","soap","underwater","fun","carbonation","sparkling"]},"toothbrush":{"a":"Toothbrush","b":"1FAA5","j":["bathroom","brush","clean","dental","hygiene","teeth"]},"sponge":{"a":"Sponge","b":"1F9FD","j":["absorbing","cleaning","porous"]},"fire-extinguisher":{"a":"Fire Extinguisher","b":"1F9EF","j":["extinguish","fire","quench"]},"shopping-cart":{"a":"Shopping Cart","b":"1F6D2","j":["cart","shopping","trolley"]},"cigarette":{"a":"Cigarette","b":"1F6AC","j":["smoking","kills","tobacco","joint","smoke"]},"coffin":{"a":"Coffin","b":"26B0","j":["death","vampire","dead","die","rip","graveyard","cemetery","casket","funeral","box"]},"headstone":{"a":"Headstone","b":"1FAA6","j":["cemetery","grave","graveyard","tombstone","death","rip"]},"funeral-urn":{"a":"Funeral Urn","b":"26B1","j":["ashes","death","funeral","urn","dead","die","rip"]},"nazar-amulet":{"a":"Nazar Amulet","b":"1F9FF","j":["bead","charm","evil-eye","nazar","talisman"]},"hamsa":{"a":"Hamsa","b":"1FAAC","j":["amulet","Fatima","hand","Mary","Miriam","protection","religion"]},"moai":{"a":"Moai","b":"1F5FF","j":["face","moyai","statue","rock","easter island"]},"placard":{"a":"Placard","b":"1FAA7","j":["demonstration","picket","protest","sign","announcement"]},"identification-card":{"a":"Identification Card","b":"1FAAA","j":["credentials","ID","license","security","document"]},"atm-sign":{"a":"Atm Sign","b":"1F3E7","j":["ATM","ATM sign","automated","bank","teller","money","sales","cash","blue-square","payment"]},"litter-in-bin-sign":{"a":"Litter in Bin Sign","b":"1F6AE","j":["litter","litter bin","blue-square","sign","human","info"]},"potable-water":{"a":"Potable Water","b":"1F6B0","j":["drinking","potable","water","blue-square","liquid","restroom","cleaning","faucet"]},"wheelchair-symbol":{"a":"Wheelchair Symbol","b":"267F","j":["access","blue-square","disabled","accessibility"]},"mens-room":{"a":"Men’S Room","b":"1F6B9","j":["bathroom","lavatory","man","men’s room","restroom","toilet","WC","men_s_room","wc","blue-square","gender","male"]},"womens-room":{"a":"Women’S Room","b":"1F6BA","j":["bathroom","lavatory","restroom","toilet","WC","woman","women’s room","women_s_room","purple-square","female","loo","gender"]},"restroom":{"a":"Restroom","b":"1F6BB","j":["bathroom","lavatory","toilet","WC","blue-square","refresh","wc","gender"]},"baby-symbol":{"a":"Baby Symbol","b":"1F6BC","j":["baby","changing","orange-square","child"]},"water-closet":{"a":"Water Closet","b":"1F6BE","j":["bathroom","closet","lavatory","restroom","toilet","water","WC","blue-square"]},"passport-control":{"a":"Passport Control","b":"1F6C2","j":["control","passport","custom","blue-square"]},"customs":{"a":"Customs","b":"1F6C3","j":["passport","border","blue-square"]},"baggage-claim":{"a":"Baggage Claim","b":"1F6C4","j":["baggage","claim","blue-square","airport","transport"]},"left-luggage":{"a":"Left Luggage","b":"1F6C5","j":["baggage","locker","luggage","blue-square","travel"]},"warning":{"a":"Warning","b":"26A0","j":["exclamation","wip","alert","error","problem","issue"]},"children-crossing":{"a":"Children Crossing","b":"1F6B8","j":["child","crossing","pedestrian","traffic","school","warning","danger","sign","driving","yellow-diamond"]},"no-entry":{"a":"No Entry","b":"26D4","j":["entry","forbidden","no","not","prohibited","traffic","limit","security","privacy","bad","denied","stop","circle"]},"prohibited":{"a":"Prohibited","b":"1F6AB","j":["entry","forbidden","no","not","forbid","stop","limit","denied","disallow","circle"]},"no-bicycles":{"a":"No Bicycles","b":"1F6B3","j":["bicycle","bike","forbidden","no","prohibited","cyclist","circle"]},"no-smoking":{"a":"No Smoking","b":"1F6AD","j":["forbidden","no","not","prohibited","smoking","cigarette","blue-square","smell","smoke"]},"no-littering":{"a":"No Littering","b":"1F6AF","j":["forbidden","litter","no","not","prohibited","trash","bin","garbage","circle"]},"nonpotable-water":{"a":"Non-Potable Water","b":"1F6B1","j":["non-drinking","non-potable","water","non_potable_water","drink","faucet","tap","circle"]},"no-pedestrians":{"a":"No Pedestrians","b":"1F6B7","j":["forbidden","no","not","pedestrian","prohibited","rules","crossing","walking","circle"]},"no-mobile-phones":{"a":"No Mobile Phones","b":"1F4F5","j":["cell","forbidden","mobile","no","phone","iphone","mute","circle"]},"no-one-under-eighteen":{"a":"No One Under Eighteen","b":"1F51E","j":["18","age restriction","eighteen","prohibited","underage","drink","pub","night","minor","circle"]},"radioactive":{"a":"Radioactive","b":"2622","j":["sign","nuclear","danger"]},"biohazard":{"a":"Biohazard","b":"2623","j":["sign","danger"]},"up-arrow":{"a":"Up Arrow","b":"2B06","j":["arrow","cardinal","direction","north","blue-square","continue","top"]},"upright-arrow":{"a":"Up-Right Arrow","b":"2197","j":["arrow","direction","intercardinal","northeast","up-right arrow","up_right_arrow","blue-square","point","diagonal"]},"right-arrow":{"a":"Right Arrow","b":"27A1","j":["arrow","cardinal","direction","east","blue-square","next"]},"downright-arrow":{"a":"Down-Right Arrow","b":"2198","j":["arrow","direction","down-right arrow","intercardinal","southeast","down_right_arrow","blue-square","diagonal"]},"down-arrow":{"a":"Down Arrow","b":"2B07","j":["arrow","cardinal","direction","down","south","blue-square","bottom"]},"downleft-arrow":{"a":"Down-Left Arrow","b":"2199","j":["arrow","direction","down-left arrow","intercardinal","southwest","down_left_arrow","blue-square","diagonal"]},"left-arrow":{"a":"Left Arrow","b":"2B05","j":["arrow","cardinal","direction","west","blue-square","previous","back"]},"upleft-arrow":{"a":"Up-Left Arrow","b":"2196","j":["arrow","direction","intercardinal","northwest","up-left arrow","up_left_arrow","blue-square","point","diagonal"]},"updown-arrow":{"a":"Up-Down Arrow","b":"2195","j":["arrow","up-down arrow","up_down_arrow","blue-square","direction","way","vertical"]},"leftright-arrow":{"a":"Left-Right Arrow","b":"2194","j":["arrow","left-right arrow","left_right_arrow","shape","direction","horizontal","sideways"]},"right-arrow-curving-left":{"a":"Right Arrow Curving Left","b":"21A9","j":["arrow","back","return","blue-square","undo","enter"]},"left-arrow-curving-right":{"a":"Left Arrow Curving Right","b":"21AA","j":["arrow","blue-square","return","rotate","direction"]},"right-arrow-curving-up":{"a":"Right Arrow Curving Up","b":"2934","j":["arrow","blue-square","direction","top"]},"right-arrow-curving-down":{"a":"Right Arrow Curving Down","b":"2935","j":["arrow","down","blue-square","direction","bottom"]},"clockwise-vertical-arrows":{"a":"Clockwise Vertical Arrows","b":"1F503","j":["arrow","clockwise","reload","sync","cycle","round","repeat"]},"counterclockwise-arrows-button":{"a":"Counterclockwise Arrows Button","b":"1F504","j":["anticlockwise","arrow","counterclockwise","withershins","blue-square","sync","cycle"]},"back-arrow":{"a":"Back Arrow","b":"1F519","j":["arrow","BACK","words","return"]},"end-arrow":{"a":"End Arrow","b":"1F51A","j":["arrow","END","words"]},"on-arrow":{"a":"On! Arrow","b":"1F51B","j":["arrow","mark","ON","ON!","words"]},"soon-arrow":{"a":"Soon Arrow","b":"1F51C","j":["arrow","SOON","words"]},"top-arrow":{"a":"Top Arrow","b":"1F51D","j":["arrow","TOP","up","words","blue-square"]},"place-of-worship":{"a":"Place of Worship","b":"1F6D0","j":["religion","worship","church","temple","prayer"]},"atom-symbol":{"a":"Atom Symbol","b":"269B","j":["atheist","atom","science","physics","chemistry"]},"om":{"a":"Om","b":"1F549","j":["Hindu","religion","hinduism","buddhism","sikhism","jainism"]},"star-of-david":{"a":"Star of David","b":"2721","j":["David","Jew","Jewish","religion","star","star of David","judaism"]},"wheel-of-dharma":{"a":"Wheel of Dharma","b":"2638","j":["Buddhist","dharma","religion","wheel","hinduism","buddhism","sikhism","jainism"]},"yin-yang":{"a":"Yin Yang","b":"262F","j":["religion","tao","taoist","yang","yin","balance"]},"latin-cross":{"a":"Latin Cross","b":"271D","j":["Christian","cross","religion","christianity"]},"orthodox-cross":{"a":"Orthodox Cross","b":"2626","j":["Christian","cross","religion","suppedaneum"]},"star-and-crescent":{"a":"Star and Crescent","b":"262A","j":["islam","Muslim","religion"]},"peace-symbol":{"a":"Peace Symbol","b":"262E","j":["peace","hippie"]},"menorah":{"a":"Menorah","b":"1F54E","j":["candelabrum","candlestick","religion","hanukkah","candles","jewish"]},"dotted-sixpointed-star":{"a":"Dotted Six-Pointed Star","b":"1F52F","j":["dotted six-pointed star","fortune","star","dotted_six_pointed_star","purple-square","religion","jewish","hexagram"]},"khanda":{"a":"⊛ Khanda","b":"1FAAF","j":["khanda","religion","Sikh"]},"aries":{"a":"Aries","b":"2648","j":["ram","zodiac","sign","purple-square","astrology"]},"taurus":{"a":"Taurus","b":"2649","j":["bull","ox","zodiac","purple-square","sign","astrology"]},"gemini":{"a":"Gemini","b":"264A","j":["twins","zodiac","sign","purple-square","astrology"]},"cancer":{"a":"Cancer","b":"264B","j":["crab","zodiac","sign","purple-square","astrology"]},"leo":{"a":"Leo","b":"264C","j":["lion","zodiac","sign","purple-square","astrology"]},"virgo":{"a":"Virgo","b":"264D","j":["zodiac","sign","purple-square","astrology"]},"libra":{"a":"Libra","b":"264E","j":["balance","justice","scales","zodiac","sign","purple-square","astrology"]},"scorpio":{"a":"Scorpio","b":"264F","j":["scorpion","scorpius","zodiac","sign","purple-square","astrology"]},"sagittarius":{"a":"Sagittarius","b":"2650","j":["archer","zodiac","sign","purple-square","astrology"]},"capricorn":{"a":"Capricorn","b":"2651","j":["goat","zodiac","sign","purple-square","astrology"]},"aquarius":{"a":"Aquarius","b":"2652","j":["bearer","water","zodiac","sign","purple-square","astrology"]},"pisces":{"a":"Pisces","b":"2653","j":["fish","zodiac","purple-square","sign","astrology"]},"ophiuchus":{"a":"Ophiuchus","b":"26CE","j":["bearer","serpent","snake","zodiac","sign","purple-square","constellation","astrology"]},"shuffle-tracks-button":{"a":"Shuffle Tracks Button","b":"1F500","j":["arrow","crossed","blue-square","shuffle","music","random"]},"repeat-button":{"a":"Repeat Button","b":"1F501","j":["arrow","clockwise","repeat","loop","record"]},"repeat-single-button":{"a":"Repeat Single Button","b":"1F502","j":["arrow","clockwise","once","blue-square","loop"]},"play-button":{"a":"Play Button","b":"25B6","j":["arrow","play","right","triangle","blue-square","direction"]},"fastforward-button":{"a":"Fast-Forward Button","b":"23E9","j":["arrow","double","fast","fast-forward button","forward","fast_forward_button","blue-square","play","speed","continue"]},"next-track-button":{"a":"Next Track Button","b":"23ED","j":["arrow","next scene","next track","triangle","forward","next","blue-square"]},"play-or-pause-button":{"a":"Play or Pause Button","b":"23EF","j":["arrow","pause","play","right","triangle","blue-square"]},"reverse-button":{"a":"Reverse Button","b":"25C0","j":["arrow","left","reverse","triangle","blue-square","direction"]},"fast-reverse-button":{"a":"Fast Reverse Button","b":"23EA","j":["arrow","double","rewind","play","blue-square"]},"last-track-button":{"a":"Last Track Button","b":"23EE","j":["arrow","previous scene","previous track","triangle","backward"]},"upwards-button":{"a":"Upwards Button","b":"1F53C","j":["arrow","button","blue-square","triangle","direction","point","forward","top"]},"fast-up-button":{"a":"Fast Up Button","b":"23EB","j":["arrow","double","blue-square","direction","top"]},"downwards-button":{"a":"Downwards Button","b":"1F53D","j":["arrow","button","down","blue-square","direction","bottom"]},"fast-down-button":{"a":"Fast Down Button","b":"23EC","j":["arrow","double","down","blue-square","direction","bottom"]},"pause-button":{"a":"Pause Button","b":"23F8","j":["bar","double","pause","vertical","blue-square"]},"stop-button":{"a":"Stop Button","b":"23F9","j":["square","stop","blue-square"]},"record-button":{"a":"Record Button","b":"23FA","j":["circle","record","blue-square"]},"eject-button":{"a":"Eject Button","b":"23CF","j":["eject","blue-square"]},"cinema":{"a":"Cinema","b":"1F3A6","j":["camera","film","movie","blue-square","record","curtain","stage","theater"]},"dim-button":{"a":"Dim Button","b":"1F505","j":["brightness","dim","low","sun","afternoon","warm","summer"]},"bright-button":{"a":"Bright Button","b":"1F506","j":["bright","brightness","sun","light"]},"antenna-bars":{"a":"Antenna Bars","b":"1F4F6","j":["antenna","bar","cell","mobile","phone","blue-square","reception","internet","connection","wifi","bluetooth","bars"]},"wireless":{"a":"⊛ Wireless","b":"1F6DC","j":["computer","internet","network","wireless"]},"vibration-mode":{"a":"Vibration Mode","b":"1F4F3","j":["cell","mobile","mode","phone","telephone","vibration","orange-square"]},"mobile-phone-off":{"a":"Mobile Phone off","b":"1F4F4","j":["cell","mobile","off","phone","telephone","mute","orange-square","silence","quiet"]},"female-sign":{"a":"Female Sign","b":"2640","j":["woman","women","lady","girl"]},"male-sign":{"a":"Male Sign","b":"2642","j":["man","boy","men"]},"transgender-symbol":{"a":"Transgender Symbol","b":"26A7","j":["transgender","lgbtq"]},"multiply":{"a":"Multiply","b":"2716","j":["×","cancel","multiplication","sign","x","multiplication_sign","math","calculation"]},"plus":{"a":"Plus","b":"2795","j":["+","math","sign","plus_sign","calculation","addition","more","increase"]},"minus":{"a":"Minus","b":"2796","j":["-","−","math","sign","minus_sign","calculation","subtract","less"]},"divide":{"a":"Divide","b":"2797","j":["÷","division","math","sign","division_sign","calculation"]},"heavy-equals-sign":{"a":"Heavy Equals Sign","b":"1F7F0","j":["equality","math"]},"infinity":{"a":"Infinity","b":"267E","j":["forever","unbounded","universal"]},"double-exclamation-mark":{"a":"Double Exclamation Mark","b":"203C","j":["!","!!","bangbang","exclamation","mark","surprise"]},"exclamation-question-mark":{"a":"Exclamation Question Mark","b":"2049","j":["!","!?","?","exclamation","interrobang","mark","punctuation","question","wat","surprise"]},"red-question-mark":{"a":"Red Question Mark","b":"2753","j":["?","mark","punctuation","question","question_mark","doubt","confused"]},"white-question-mark":{"a":"White Question Mark","b":"2754","j":["?","mark","outlined","punctuation","question","doubts","gray","huh","confused"]},"white-exclamation-mark":{"a":"White Exclamation Mark","b":"2755","j":["!","exclamation","mark","outlined","punctuation","surprise","gray","wow","warning"]},"red-exclamation-mark":{"a":"Red Exclamation Mark","b":"2757","j":["!","exclamation","mark","punctuation","exclamation_mark","heavy_exclamation_mark","danger","surprise","wow","warning"]},"wavy-dash":{"a":"Wavy Dash","b":"3030","j":["dash","punctuation","wavy","draw","line","moustache","mustache","squiggle","scribble"]},"currency-exchange":{"a":"Currency Exchange","b":"1F4B1","j":["bank","currency","exchange","money","sales","dollar","travel"]},"heavy-dollar-sign":{"a":"Heavy Dollar Sign","b":"1F4B2","j":["currency","dollar","money","sales","payment","buck"]},"medical-symbol":{"a":"Medical Symbol","b":"2695","j":["aesculapius","medicine","staff","health","hospital"]},"recycling-symbol":{"a":"Recycling Symbol","b":"267B","j":["recycle","arrow","environment","garbage","trash"]},"fleurdelis":{"a":"Fleur-De-Lis","b":"269C","j":["fleur-de-lis","fleur_de_lis","decorative","scout"]},"trident-emblem":{"a":"Trident Emblem","b":"1F531","j":["anchor","emblem","ship","tool","trident","weapon","spear"]},"name-badge":{"a":"Name Badge","b":"1F4DB","j":["badge","name","fire","forbid"]},"japanese-symbol-for-beginner":{"a":"Japanese Symbol for Beginner","b":"1F530","j":["beginner","chevron","Japanese","Japanese symbol for beginner","leaf","badge","shield"]},"hollow-red-circle":{"a":"Hollow Red Circle","b":"2B55","j":["circle","large","o","red","round"]},"check-mark-button":{"a":"Check Mark Button","b":"2705","j":["✓","button","check","mark","green-square","ok","agree","vote","election","answer","tick"]},"check-box-with-check":{"a":"Check Box with Check","b":"2611","j":["✓","box","check","ok","agree","confirm","black-square","vote","election","yes","tick"]},"check-mark":{"a":"Check Mark","b":"2714","j":["✓","check","mark","ok","nike","answer","yes","tick"]},"cross-mark":{"a":"Cross Mark","b":"274C","j":["×","cancel","cross","mark","multiplication","multiply","x","no","delete","remove","red"]},"cross-mark-button":{"a":"Cross Mark Button","b":"274E","j":["×","mark","square","x","green-square","no","deny"]},"curly-loop":{"a":"Curly Loop","b":"27B0","j":["curl","loop","scribble","draw","shape","squiggle"]},"double-curly-loop":{"a":"Double Curly Loop","b":"27BF","j":["curl","double","loop","tape","cassette"]},"part-alternation-mark":{"a":"Part Alternation Mark","b":"303D","j":["mark","part","graph","presentation","stats","business","economics","bad"]},"eightspoked-asterisk":{"a":"Eight-Spoked Asterisk","b":"2733","j":["*","asterisk","eight-spoked asterisk","eight_spoked_asterisk","star","sparkle","green-square"]},"eightpointed-star":{"a":"Eight-Pointed Star","b":"2734","j":["*","eight-pointed star","star","eight_pointed_star","orange-square","shape","polygon"]},"sparkle":{"a":"Sparkle","b":"2747","j":["*","stars","green-square","awesome","good","fireworks"]},"copyright":{"a":"Copyright","b":"00A9","j":["C","ip","license","circle","law","legal"]},"registered":{"a":"Registered","b":"00AE","j":["R","alphabet","circle"]},"trade-mark":{"a":"Trade Mark","b":"2122","j":["mark","TM","trademark","brand","law","legal"]},"keycap":{"a":"Keycap: *","b":"002A-FE0F-20E3","j":["keycap_","star"]},"keycap-0":{"a":"Keycap: 0","b":"0030-FE0F-20E3","j":["keycap","0","numbers","blue-square","null"]},"keycap-1":{"a":"Keycap: 1","b":"0031-FE0F-20E3","j":["keycap","blue-square","numbers","1"]},"keycap-2":{"a":"Keycap: 2","b":"0032-FE0F-20E3","j":["keycap","numbers","2","prime","blue-square"]},"keycap-3":{"a":"Keycap: 3","b":"0033-FE0F-20E3","j":["keycap","3","numbers","prime","blue-square"]},"keycap-4":{"a":"Keycap: 4","b":"0034-FE0F-20E3","j":["keycap","4","numbers","blue-square"]},"keycap-5":{"a":"Keycap: 5","b":"0035-FE0F-20E3","j":["keycap","5","numbers","blue-square","prime"]},"keycap-6":{"a":"Keycap: 6","b":"0036-FE0F-20E3","j":["keycap","6","numbers","blue-square"]},"keycap-7":{"a":"Keycap: 7","b":"0037-FE0F-20E3","j":["keycap","7","numbers","blue-square","prime"]},"keycap-8":{"a":"Keycap: 8","b":"0038-FE0F-20E3","j":["keycap","8","blue-square","numbers"]},"keycap-9":{"a":"Keycap: 9","b":"0039-FE0F-20E3","j":["keycap","blue-square","numbers","9"]},"keycap-10":{"a":"Keycap: 10","b":"1F51F","j":["keycap","numbers","10","blue-square"]},"input-latin-uppercase":{"a":"Input Latin Uppercase","b":"1F520","j":["ABCD","input","latin","letters","uppercase","alphabet","words","blue-square"]},"input-latin-lowercase":{"a":"Input Latin Lowercase","b":"1F521","j":["abcd","input","latin","letters","lowercase","blue-square","alphabet"]},"input-numbers":{"a":"Input Numbers","b":"1F522","j":["1234","input","numbers","blue-square"]},"input-symbols":{"a":"Input Symbols","b":"1F523","j":["〒♪&%","input","blue-square","music","note","ampersand","percent","glyphs","characters"]},"input-latin-letters":{"a":"Input Latin Letters","b":"1F524","j":["abc","alphabet","input","latin","letters","blue-square"]},"a-button-blood-type":{"a":"A Button (Blood Type)","b":"1F170","j":["A","A button (blood type)","blood type","a_button","red-square","alphabet","letter"]},"ab-button-blood-type":{"a":"Ab Button (Blood Type)","b":"1F18E","j":["AB","AB button (blood type)","blood type","ab_button","red-square","alphabet"]},"b-button-blood-type":{"a":"B Button (Blood Type)","b":"1F171","j":["B","B button (blood type)","blood type","b_button","red-square","alphabet","letter"]},"cl-button":{"a":"Cl Button","b":"1F191","j":["CL","CL button","alphabet","words","red-square"]},"cool-button":{"a":"Cool Button","b":"1F192","j":["COOL","COOL button","words","blue-square"]},"free-button":{"a":"Free Button","b":"1F193","j":["FREE","FREE button","blue-square","words"]},"information":{"a":"Information","b":"2139","j":["i","blue-square","alphabet","letter"]},"id-button":{"a":"Id Button","b":"1F194","j":["ID","ID button","identity","purple-square","words"]},"circled-m":{"a":"Circled M","b":"24C2","j":["circle","circled M","M","alphabet","blue-circle","letter"]},"new-button":{"a":"New Button","b":"1F195","j":["NEW","NEW button","blue-square","words","start"]},"ng-button":{"a":"Ng Button","b":"1F196","j":["NG","NG button","blue-square","words","shape","icon"]},"o-button-blood-type":{"a":"O Button (Blood Type)","b":"1F17E","j":["blood type","O","O button (blood type)","o_button","alphabet","red-square","letter"]},"ok-button":{"a":"Ok Button","b":"1F197","j":["OK","OK button","good","agree","yes","blue-square"]},"p-button":{"a":"P Button","b":"1F17F","j":["P","P button","parking","cars","blue-square","alphabet","letter"]},"sos-button":{"a":"Sos Button","b":"1F198","j":["help","SOS","SOS button","red-square","words","emergency","911"]},"up-button":{"a":"Up! Button","b":"1F199","j":["mark","UP","UP!","UP! button","blue-square","above","high"]},"vs-button":{"a":"Vs Button","b":"1F19A","j":["versus","VS","VS button","words","orange-square"]},"japanese-here-button":{"a":"Japanese “Here” Button","b":"1F201","j":["“here”","Japanese","Japanese “here” button","katakana","ココ","blue-square","here","japanese","destination"]},"japanese-service-charge-button":{"a":"Japanese “Service Charge” Button","b":"1F202","j":["“service charge”","Japanese","Japanese “service charge” button","katakana","サ","japanese","blue-square"]},"japanese-monthly-amount-button":{"a":"Japanese “Monthly Amount” Button","b":"1F237","j":["“monthly amount”","ideograph","Japanese","Japanese “monthly amount” button","月","chinese","month","moon","japanese","orange-square","kanji"]},"japanese-not-free-of-charge-button":{"a":"Japanese “Not Free of Charge” Button","b":"1F236","j":["“not free of charge”","ideograph","Japanese","Japanese “not free of charge” button","有","orange-square","chinese","have","kanji"]},"japanese-reserved-button":{"a":"Japanese “Reserved” Button","b":"1F22F","j":["“reserved”","ideograph","Japanese","Japanese “reserved” button","指","chinese","point","green-square","kanji"]},"japanese-bargain-button":{"a":"Japanese “Bargain” Button","b":"1F250","j":["“bargain”","ideograph","Japanese","Japanese “bargain” button","得","chinese","kanji","obtain","get","circle"]},"japanese-discount-button":{"a":"Japanese “Discount” Button","b":"1F239","j":["“discount”","ideograph","Japanese","Japanese “discount” button","割","cut","divide","chinese","kanji","pink-square"]},"japanese-free-of-charge-button":{"a":"Japanese “Free of Charge” Button","b":"1F21A","j":["“free of charge”","ideograph","Japanese","Japanese “free of charge” button","無","nothing","chinese","kanji","japanese","orange-square"]},"japanese-prohibited-button":{"a":"Japanese “Prohibited” Button","b":"1F232","j":["“prohibited”","ideograph","Japanese","Japanese “prohibited” button","禁","kanji","japanese","chinese","forbidden","limit","restricted","red-square"]},"japanese-acceptable-button":{"a":"Japanese “Acceptable” Button","b":"1F251","j":["“acceptable”","ideograph","Japanese","Japanese “acceptable” button","可","ok","good","chinese","kanji","agree","yes","orange-circle"]},"japanese-application-button":{"a":"Japanese “Application” Button","b":"1F238","j":["“application”","ideograph","Japanese","Japanese “application” button","申","chinese","japanese","kanji","orange-square"]},"japanese-passing-grade-button":{"a":"Japanese “Passing Grade” Button","b":"1F234","j":["“passing grade”","ideograph","Japanese","Japanese “passing grade” button","合","japanese","chinese","join","kanji","red-square"]},"japanese-vacancy-button":{"a":"Japanese “Vacancy” Button","b":"1F233","j":["“vacancy”","ideograph","Japanese","Japanese “vacancy” button","空","kanji","japanese","chinese","empty","sky","blue-square"]},"japanese-congratulations-button":{"a":"Japanese “Congratulations” Button","b":"3297","j":["“congratulations”","ideograph","Japanese","Japanese “congratulations” button","祝","chinese","kanji","japanese","red-circle"]},"japanese-secret-button":{"a":"Japanese “Secret” Button","b":"3299","j":["“secret”","ideograph","Japanese","Japanese “secret” button","秘","privacy","chinese","sshh","kanji","red-circle"]},"japanese-open-for-business-button":{"a":"Japanese “Open for Business” Button","b":"1F23A","j":["“open for business”","ideograph","Japanese","Japanese “open for business” button","営","japanese","opening hours","orange-square"]},"japanese-no-vacancy-button":{"a":"Japanese “No Vacancy” Button","b":"1F235","j":["“no vacancy”","ideograph","Japanese","Japanese “no vacancy” button","満","full","chinese","japanese","red-square","kanji"]},"red-circle":{"a":"Red Circle","b":"1F534","j":["circle","geometric","red","shape","error","danger"]},"orange-circle":{"a":"Orange Circle","b":"1F7E0","j":["circle","orange","round"]},"yellow-circle":{"a":"Yellow Circle","b":"1F7E1","j":["circle","yellow","round"]},"green-circle":{"a":"Green Circle","b":"1F7E2","j":["circle","green","round"]},"blue-circle":{"a":"Blue Circle","b":"1F535","j":["blue","circle","geometric","shape","icon","button"]},"purple-circle":{"a":"Purple Circle","b":"1F7E3","j":["circle","purple","round"]},"brown-circle":{"a":"Brown Circle","b":"1F7E4","j":["brown","circle","round"]},"black-circle":{"a":"Black Circle","b":"26AB","j":["circle","geometric","shape","button","round"]},"white-circle":{"a":"White Circle","b":"26AA","j":["circle","geometric","shape","round"]},"red-square":{"a":"Red Square","b":"1F7E5","j":["red","square"]},"orange-square":{"a":"Orange Square","b":"1F7E7","j":["orange","square"]},"yellow-square":{"a":"Yellow Square","b":"1F7E8","j":["square","yellow"]},"green-square":{"a":"Green Square","b":"1F7E9","j":["green","square"]},"blue-square":{"a":"Blue Square","b":"1F7E6","j":["blue","square"]},"purple-square":{"a":"Purple Square","b":"1F7EA","j":["purple","square"]},"brown-square":{"a":"Brown Square","b":"1F7EB","j":["brown","square"]},"black-large-square":{"a":"Black Large Square","b":"2B1B","j":["geometric","square","shape","icon","button"]},"white-large-square":{"a":"White Large Square","b":"2B1C","j":["geometric","square","shape","icon","stone","button"]},"black-medium-square":{"a":"Black Medium Square","b":"25FC","j":["geometric","square","shape","button","icon"]},"white-medium-square":{"a":"White Medium Square","b":"25FB","j":["geometric","square","shape","stone","icon"]},"black-mediumsmall-square":{"a":"Black Medium-Small Square","b":"25FE","j":["black medium-small square","geometric","square","black_medium_small_square","icon","shape","button"]},"white-mediumsmall-square":{"a":"White Medium-Small Square","b":"25FD","j":["geometric","square","white medium-small square","white_medium_small_square","shape","stone","icon","button"]},"black-small-square":{"a":"Black Small Square","b":"25AA","j":["geometric","square","shape","icon"]},"white-small-square":{"a":"White Small Square","b":"25AB","j":["geometric","square","shape","icon"]},"large-orange-diamond":{"a":"Large Orange Diamond","b":"1F536","j":["diamond","geometric","orange","shape","jewel","gem"]},"large-blue-diamond":{"a":"Large Blue Diamond","b":"1F537","j":["blue","diamond","geometric","shape","jewel","gem"]},"small-orange-diamond":{"a":"Small Orange Diamond","b":"1F538","j":["diamond","geometric","orange","shape","jewel","gem"]},"small-blue-diamond":{"a":"Small Blue Diamond","b":"1F539","j":["blue","diamond","geometric","shape","jewel","gem"]},"red-triangle-pointed-up":{"a":"Red Triangle Pointed Up","b":"1F53A","j":["geometric","red","shape","direction","up","top"]},"red-triangle-pointed-down":{"a":"Red Triangle Pointed Down","b":"1F53B","j":["down","geometric","red","shape","direction","bottom"]},"diamond-with-a-dot":{"a":"Diamond with a Dot","b":"1F4A0","j":["comic","diamond","geometric","inside","jewel","blue","gem","crystal","fancy"]},"radio-button":{"a":"Radio Button","b":"1F518","j":["button","geometric","radio","input","old","music","circle"]},"white-square-button":{"a":"White Square Button","b":"1F533","j":["button","geometric","outlined","square","shape","input"]},"black-square-button":{"a":"Black Square Button","b":"1F532","j":["button","geometric","square","shape","input","frame"]},"chequered-flag":{"a":"Chequered Flag","b":"1F3C1","j":["checkered","chequered","racing","contest","finishline","race","gokart"]},"triangular-flag":{"a":"Triangular Flag","b":"1F6A9","j":["post","mark","milestone","place"]},"crossed-flags":{"a":"Crossed Flags","b":"1F38C","j":["celebration","cross","crossed","Japanese","japanese","nation","country","border"]},"black-flag":{"a":"Black Flag","b":"1F3F4","j":["waving","pirate"]},"white-flag":{"a":"White Flag","b":"1F3F3","j":["waving","losing","loser","lost","surrender","give up","fail"]},"rainbow-flag":{"a":"Rainbow Flag","b":"1F3F3-FE0F-200D-1F308","j":["pride","rainbow","flag","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"]},"transgender-flag":{"a":"Transgender Flag","b":"1F3F3-FE0F-200D-26A7-FE0F","j":["flag","light blue","pink","transgender","white","lgbtq"]},"pirate-flag":{"a":"Pirate Flag","b":"1F3F4-200D-2620-FE0F","j":["Jolly Roger","pirate","plunder","treasure","skull","crossbones","flag","banner"]},"flag-ascension-island":{"a":"Flag: Ascension Island","b":"1F1E6-1F1E8","j":["flag"]},"flag-andorra":{"a":"Flag: Andorra","b":"1F1E6-1F1E9","j":["flag","ad","nation","country","banner","andorra"]},"flag-united-arab-emirates":{"a":"Flag: United Arab Emirates","b":"1F1E6-1F1EA","j":["flag","united","arab","emirates","nation","country","banner","united_arab_emirates"]},"flag-afghanistan":{"a":"Flag: Afghanistan","b":"1F1E6-1F1EB","j":["flag","af","nation","country","banner","afghanistan"]},"flag-antigua--barbuda":{"a":"Flag: Antigua & Barbuda","b":"1F1E6-1F1EC","j":["flag","flag_antigua_barbuda","antigua","barbuda","nation","country","banner","antigua_barbuda"]},"flag-anguilla":{"a":"Flag: Anguilla","b":"1F1E6-1F1EE","j":["flag","ai","nation","country","banner","anguilla"]},"flag-albania":{"a":"Flag: Albania","b":"1F1E6-1F1F1","j":["flag","al","nation","country","banner","albania"]},"flag-armenia":{"a":"Flag: Armenia","b":"1F1E6-1F1F2","j":["flag","am","nation","country","banner","armenia"]},"flag-angola":{"a":"Flag: Angola","b":"1F1E6-1F1F4","j":["flag","ao","nation","country","banner","angola"]},"flag-antarctica":{"a":"Flag: Antarctica","b":"1F1E6-1F1F6","j":["flag","aq","nation","country","banner","antarctica"]},"flag-argentina":{"a":"Flag: Argentina","b":"1F1E6-1F1F7","j":["flag","ar","nation","country","banner","argentina"]},"flag-american-samoa":{"a":"Flag: American Samoa","b":"1F1E6-1F1F8","j":["flag","american","ws","nation","country","banner","american_samoa"]},"flag-austria":{"a":"Flag: Austria","b":"1F1E6-1F1F9","j":["flag","at","nation","country","banner","austria"]},"flag-australia":{"a":"Flag: Australia","b":"1F1E6-1F1FA","j":["flag","au","nation","country","banner","australia"]},"flag-aruba":{"a":"Flag: Aruba","b":"1F1E6-1F1FC","j":["flag","aw","nation","country","banner","aruba"]},"flag-land-islands":{"a":"Flag: Åland Islands","b":"1F1E6-1F1FD","j":["flag","flag_aland_islands","Åland","islands","nation","country","banner","aland_islands"]},"flag-azerbaijan":{"a":"Flag: Azerbaijan","b":"1F1E6-1F1FF","j":["flag","az","nation","country","banner","azerbaijan"]},"flag-bosnia--herzegovina":{"a":"Flag: Bosnia & Herzegovina","b":"1F1E7-1F1E6","j":["flag","flag_bosnia_herzegovina","bosnia","herzegovina","nation","country","banner","bosnia_herzegovina"]},"flag-barbados":{"a":"Flag: Barbados","b":"1F1E7-1F1E7","j":["flag","bb","nation","country","banner","barbados"]},"flag-bangladesh":{"a":"Flag: Bangladesh","b":"1F1E7-1F1E9","j":["flag","bd","nation","country","banner","bangladesh"]},"flag-belgium":{"a":"Flag: Belgium","b":"1F1E7-1F1EA","j":["flag","be","nation","country","banner","belgium"]},"flag-burkina-faso":{"a":"Flag: Burkina Faso","b":"1F1E7-1F1EB","j":["flag","burkina","faso","nation","country","banner","burkina_faso"]},"flag-bulgaria":{"a":"Flag: Bulgaria","b":"1F1E7-1F1EC","j":["flag","bg","nation","country","banner","bulgaria"]},"flag-bahrain":{"a":"Flag: Bahrain","b":"1F1E7-1F1ED","j":["flag","bh","nation","country","banner","bahrain"]},"flag-burundi":{"a":"Flag: Burundi","b":"1F1E7-1F1EE","j":["flag","bi","nation","country","banner","burundi"]},"flag-benin":{"a":"Flag: Benin","b":"1F1E7-1F1EF","j":["flag","bj","nation","country","banner","benin"]},"flag-st-barthlemy":{"a":"Flag: St. Barthélemy","b":"1F1E7-1F1F1","j":["flag","flag_st_barthelemy","saint","barthélemy","nation","country","banner","st_barthelemy"]},"flag-bermuda":{"a":"Flag: Bermuda","b":"1F1E7-1F1F2","j":["flag","bm","nation","country","banner","bermuda"]},"flag-brunei":{"a":"Flag: Brunei","b":"1F1E7-1F1F3","j":["flag","bn","darussalam","nation","country","banner","brunei"]},"flag-bolivia":{"a":"Flag: Bolivia","b":"1F1E7-1F1F4","j":["flag","bo","nation","country","banner","bolivia"]},"flag-caribbean-netherlands":{"a":"Flag: Caribbean Netherlands","b":"1F1E7-1F1F6","j":["flag","bonaire","nation","country","banner","caribbean_netherlands"]},"flag-brazil":{"a":"Flag: Brazil","b":"1F1E7-1F1F7","j":["flag","br","nation","country","banner","brazil"]},"flag-bahamas":{"a":"Flag: Bahamas","b":"1F1E7-1F1F8","j":["flag","bs","nation","country","banner","bahamas"]},"flag-bhutan":{"a":"Flag: Bhutan","b":"1F1E7-1F1F9","j":["flag","bt","nation","country","banner","bhutan"]},"flag-bouvet-island":{"a":"Flag: Bouvet Island","b":"1F1E7-1F1FB","j":["flag","norway"]},"flag-botswana":{"a":"Flag: Botswana","b":"1F1E7-1F1FC","j":["flag","bw","nation","country","banner","botswana"]},"flag-belarus":{"a":"Flag: Belarus","b":"1F1E7-1F1FE","j":["flag","by","nation","country","banner","belarus"]},"flag-belize":{"a":"Flag: Belize","b":"1F1E7-1F1FF","j":["flag","bz","nation","country","banner","belize"]},"flag-canada":{"a":"Flag: Canada","b":"1F1E8-1F1E6","j":["flag","ca","nation","country","banner","canada"]},"flag-cocos-keeling-islands":{"a":"Flag: Cocos (Keeling) Islands","b":"1F1E8-1F1E8","j":["flag","flag_cocos_islands","cocos","keeling","islands","nation","country","banner","cocos_islands"]},"flag-congo--kinshasa":{"a":"Flag: Congo - Kinshasa","b":"1F1E8-1F1E9","j":["flag","flag_congo_kinshasa","congo","democratic","republic","nation","country","banner","congo_kinshasa"]},"flag-central-african-republic":{"a":"Flag: Central African Republic","b":"1F1E8-1F1EB","j":["flag","central","african","republic","nation","country","banner","central_african_republic"]},"flag-congo--brazzaville":{"a":"Flag: Congo - Brazzaville","b":"1F1E8-1F1EC","j":["flag","flag_congo_brazzaville","congo","nation","country","banner","congo_brazzaville"]},"flag-switzerland":{"a":"Flag: Switzerland","b":"1F1E8-1F1ED","j":["flag","ch","nation","country","banner","switzerland"]},"flag-cte-divoire":{"a":"Flag: Côte D’Ivoire","b":"1F1E8-1F1EE","j":["flag","flag_cote_d_ivoire","ivory","coast","nation","country","banner","cote_d_ivoire"]},"flag-cook-islands":{"a":"Flag: Cook Islands","b":"1F1E8-1F1F0","j":["flag","cook","islands","nation","country","banner","cook_islands"]},"flag-chile":{"a":"Flag: Chile","b":"1F1E8-1F1F1","j":["flag","nation","country","banner","chile"]},"flag-cameroon":{"a":"Flag: Cameroon","b":"1F1E8-1F1F2","j":["flag","cm","nation","country","banner","cameroon"]},"flag-china":{"a":"Flag: China","b":"1F1E8-1F1F3","j":["flag","china","chinese","prc","country","nation","banner"]},"flag-colombia":{"a":"Flag: Colombia","b":"1F1E8-1F1F4","j":["flag","co","nation","country","banner","colombia"]},"flag-clipperton-island":{"a":"Flag: Clipperton Island","b":"1F1E8-1F1F5","j":["flag"]},"flag-costa-rica":{"a":"Flag: Costa Rica","b":"1F1E8-1F1F7","j":["flag","costa","rica","nation","country","banner","costa_rica"]},"flag-cuba":{"a":"Flag: Cuba","b":"1F1E8-1F1FA","j":["flag","cu","nation","country","banner","cuba"]},"flag-cape-verde":{"a":"Flag: Cape Verde","b":"1F1E8-1F1FB","j":["flag","cabo","verde","nation","country","banner","cape_verde"]},"flag-curaao":{"a":"Flag: Curaçao","b":"1F1E8-1F1FC","j":["flag","flag_curacao","curaçao","nation","country","banner","curacao"]},"flag-christmas-island":{"a":"Flag: Christmas Island","b":"1F1E8-1F1FD","j":["flag","christmas","island","nation","country","banner","christmas_island"]},"flag-cyprus":{"a":"Flag: Cyprus","b":"1F1E8-1F1FE","j":["flag","cy","nation","country","banner","cyprus"]},"flag-czechia":{"a":"Flag: Czechia","b":"1F1E8-1F1FF","j":["flag","cz","nation","country","banner","czechia"]},"flag-germany":{"a":"Flag: Germany","b":"1F1E9-1F1EA","j":["flag","german","nation","country","banner","germany"]},"flag-diego-garcia":{"a":"Flag: Diego Garcia","b":"1F1E9-1F1EC","j":["flag"]},"flag-djibouti":{"a":"Flag: Djibouti","b":"1F1E9-1F1EF","j":["flag","dj","nation","country","banner","djibouti"]},"flag-denmark":{"a":"Flag: Denmark","b":"1F1E9-1F1F0","j":["flag","dk","nation","country","banner","denmark"]},"flag-dominica":{"a":"Flag: Dominica","b":"1F1E9-1F1F2","j":["flag","dm","nation","country","banner","dominica"]},"flag-dominican-republic":{"a":"Flag: Dominican Republic","b":"1F1E9-1F1F4","j":["flag","dominican","republic","nation","country","banner","dominican_republic"]},"flag-algeria":{"a":"Flag: Algeria","b":"1F1E9-1F1FF","j":["flag","dz","nation","country","banner","algeria"]},"flag-ceuta--melilla":{"a":"Flag: Ceuta & Melilla","b":"1F1EA-1F1E6","j":["flag","flag_ceuta_melilla"]},"flag-ecuador":{"a":"Flag: Ecuador","b":"1F1EA-1F1E8","j":["flag","ec","nation","country","banner","ecuador"]},"flag-estonia":{"a":"Flag: Estonia","b":"1F1EA-1F1EA","j":["flag","ee","nation","country","banner","estonia"]},"flag-egypt":{"a":"Flag: Egypt","b":"1F1EA-1F1EC","j":["flag","eg","nation","country","banner","egypt"]},"flag-western-sahara":{"a":"Flag: Western Sahara","b":"1F1EA-1F1ED","j":["flag","western","sahara","nation","country","banner","western_sahara"]},"flag-eritrea":{"a":"Flag: Eritrea","b":"1F1EA-1F1F7","j":["flag","er","nation","country","banner","eritrea"]},"flag-spain":{"a":"Flag: Spain","b":"1F1EA-1F1F8","j":["flag","spain","nation","country","banner"]},"flag-ethiopia":{"a":"Flag: Ethiopia","b":"1F1EA-1F1F9","j":["flag","et","nation","country","banner","ethiopia"]},"flag-european-union":{"a":"Flag: European Union","b":"1F1EA-1F1FA","j":["flag","european","union","banner"]},"flag-finland":{"a":"Flag: Finland","b":"1F1EB-1F1EE","j":["flag","fi","nation","country","banner","finland"]},"flag-fiji":{"a":"Flag: Fiji","b":"1F1EB-1F1EF","j":["flag","fj","nation","country","banner","fiji"]},"flag-falkland-islands":{"a":"Flag: Falkland Islands","b":"1F1EB-1F1F0","j":["flag","falkland","islands","malvinas","nation","country","banner","falkland_islands"]},"flag-micronesia":{"a":"Flag: Micronesia","b":"1F1EB-1F1F2","j":["flag","micronesia","federated","states","nation","country","banner"]},"flag-faroe-islands":{"a":"Flag: Faroe Islands","b":"1F1EB-1F1F4","j":["flag","faroe","islands","nation","country","banner","faroe_islands"]},"flag-france":{"a":"Flag: France","b":"1F1EB-1F1F7","j":["flag","banner","nation","france","french","country"]},"flag-gabon":{"a":"Flag: Gabon","b":"1F1EC-1F1E6","j":["flag","ga","nation","country","banner","gabon"]},"flag-united-kingdom":{"a":"Flag: United Kingdom","b":"1F1EC-1F1E7","j":["flag","united","kingdom","great","britain","northern","ireland","nation","country","banner","british","UK","english","england","union jack","united_kingdom"]},"flag-grenada":{"a":"Flag: Grenada","b":"1F1EC-1F1E9","j":["flag","gd","nation","country","banner","grenada"]},"flag-georgia":{"a":"Flag: Georgia","b":"1F1EC-1F1EA","j":["flag","ge","nation","country","banner","georgia"]},"flag-french-guiana":{"a":"Flag: French Guiana","b":"1F1EC-1F1EB","j":["flag","french","guiana","nation","country","banner","french_guiana"]},"flag-guernsey":{"a":"Flag: Guernsey","b":"1F1EC-1F1EC","j":["flag","gg","nation","country","banner","guernsey"]},"flag-ghana":{"a":"Flag: Ghana","b":"1F1EC-1F1ED","j":["flag","gh","nation","country","banner","ghana"]},"flag-gibraltar":{"a":"Flag: Gibraltar","b":"1F1EC-1F1EE","j":["flag","gi","nation","country","banner","gibraltar"]},"flag-greenland":{"a":"Flag: Greenland","b":"1F1EC-1F1F1","j":["flag","gl","nation","country","banner","greenland"]},"flag-gambia":{"a":"Flag: Gambia","b":"1F1EC-1F1F2","j":["flag","gm","nation","country","banner","gambia"]},"flag-guinea":{"a":"Flag: Guinea","b":"1F1EC-1F1F3","j":["flag","gn","nation","country","banner","guinea"]},"flag-guadeloupe":{"a":"Flag: Guadeloupe","b":"1F1EC-1F1F5","j":["flag","gp","nation","country","banner","guadeloupe"]},"flag-equatorial-guinea":{"a":"Flag: Equatorial Guinea","b":"1F1EC-1F1F6","j":["flag","equatorial","gn","nation","country","banner","equatorial_guinea"]},"flag-greece":{"a":"Flag: Greece","b":"1F1EC-1F1F7","j":["flag","gr","nation","country","banner","greece"]},"flag-south-georgia--south-sandwich-islands":{"a":"Flag: South Georgia & South Sandwich Islands","b":"1F1EC-1F1F8","j":["flag","flag_south_georgia_south_sandwich_islands","south","georgia","sandwich","islands","nation","country","banner","south_georgia_south_sandwich_islands"]},"flag-guatemala":{"a":"Flag: Guatemala","b":"1F1EC-1F1F9","j":["flag","gt","nation","country","banner","guatemala"]},"flag-guam":{"a":"Flag: Guam","b":"1F1EC-1F1FA","j":["flag","gu","nation","country","banner","guam"]},"flag-guineabissau":{"a":"Flag: Guinea-Bissau","b":"1F1EC-1F1FC","j":["flag","flag_guinea_bissau","gw","bissau","nation","country","banner","guinea_bissau"]},"flag-guyana":{"a":"Flag: Guyana","b":"1F1EC-1F1FE","j":["flag","gy","nation","country","banner","guyana"]},"flag-hong-kong-sar-china":{"a":"Flag: Hong Kong Sar China","b":"1F1ED-1F1F0","j":["flag","hong","kong","nation","country","banner","hong_kong_sar_china"]},"flag-heard--mcdonald-islands":{"a":"Flag: Heard & Mcdonald Islands","b":"1F1ED-1F1F2","j":["flag","flag_heard_mcdonald_islands"]},"flag-honduras":{"a":"Flag: Honduras","b":"1F1ED-1F1F3","j":["flag","hn","nation","country","banner","honduras"]},"flag-croatia":{"a":"Flag: Croatia","b":"1F1ED-1F1F7","j":["flag","hr","nation","country","banner","croatia"]},"flag-haiti":{"a":"Flag: Haiti","b":"1F1ED-1F1F9","j":["flag","ht","nation","country","banner","haiti"]},"flag-hungary":{"a":"Flag: Hungary","b":"1F1ED-1F1FA","j":["flag","hu","nation","country","banner","hungary"]},"flag-canary-islands":{"a":"Flag: Canary Islands","b":"1F1EE-1F1E8","j":["flag","canary","islands","nation","country","banner","canary_islands"]},"flag-indonesia":{"a":"Flag: Indonesia","b":"1F1EE-1F1E9","j":["flag","nation","country","banner","indonesia"]},"flag-ireland":{"a":"Flag: Ireland","b":"1F1EE-1F1EA","j":["flag","ie","nation","country","banner","ireland"]},"flag-israel":{"a":"Flag: Israel","b":"1F1EE-1F1F1","j":["flag","il","nation","country","banner","israel"]},"flag-isle-of-man":{"a":"Flag: Isle of Man","b":"1F1EE-1F1F2","j":["flag","isle","man","nation","country","banner","isle_of_man"]},"flag-india":{"a":"Flag: India","b":"1F1EE-1F1F3","j":["flag","in","nation","country","banner","india"]},"flag-british-indian-ocean-territory":{"a":"Flag: British Indian Ocean Territory","b":"1F1EE-1F1F4","j":["flag","british","indian","ocean","territory","nation","country","banner","british_indian_ocean_territory"]},"flag-iraq":{"a":"Flag: Iraq","b":"1F1EE-1F1F6","j":["flag","iq","nation","country","banner","iraq"]},"flag-iran":{"a":"Flag: Iran","b":"1F1EE-1F1F7","j":["flag","iran","islamic","republic","nation","country","banner"]},"flag-iceland":{"a":"Flag: Iceland","b":"1F1EE-1F1F8","j":["flag","is","nation","country","banner","iceland"]},"flag-italy":{"a":"Flag: Italy","b":"1F1EE-1F1F9","j":["flag","italy","nation","country","banner"]},"flag-jersey":{"a":"Flag: Jersey","b":"1F1EF-1F1EA","j":["flag","je","nation","country","banner","jersey"]},"flag-jamaica":{"a":"Flag: Jamaica","b":"1F1EF-1F1F2","j":["flag","jm","nation","country","banner","jamaica"]},"flag-jordan":{"a":"Flag: Jordan","b":"1F1EF-1F1F4","j":["flag","jo","nation","country","banner","jordan"]},"flag-japan":{"a":"Flag: Japan","b":"1F1EF-1F1F5","j":["flag","japanese","nation","country","banner","japan","jp","ja"]},"flag-kenya":{"a":"Flag: Kenya","b":"1F1F0-1F1EA","j":["flag","ke","nation","country","banner","kenya"]},"flag-kyrgyzstan":{"a":"Flag: Kyrgyzstan","b":"1F1F0-1F1EC","j":["flag","kg","nation","country","banner","kyrgyzstan"]},"flag-cambodia":{"a":"Flag: Cambodia","b":"1F1F0-1F1ED","j":["flag","kh","nation","country","banner","cambodia"]},"flag-kiribati":{"a":"Flag: Kiribati","b":"1F1F0-1F1EE","j":["flag","ki","nation","country","banner","kiribati"]},"flag-comoros":{"a":"Flag: Comoros","b":"1F1F0-1F1F2","j":["flag","km","nation","country","banner","comoros"]},"flag-st-kitts--nevis":{"a":"Flag: St. Kitts & Nevis","b":"1F1F0-1F1F3","j":["flag","flag_st_kitts_nevis","saint","kitts","nevis","nation","country","banner","st_kitts_nevis"]},"flag-north-korea":{"a":"Flag: North Korea","b":"1F1F0-1F1F5","j":["flag","north","korea","nation","country","banner","north_korea"]},"flag-south-korea":{"a":"Flag: South Korea","b":"1F1F0-1F1F7","j":["flag","south","korea","nation","country","banner","south_korea"]},"flag-kuwait":{"a":"Flag: Kuwait","b":"1F1F0-1F1FC","j":["flag","kw","nation","country","banner","kuwait"]},"flag-cayman-islands":{"a":"Flag: Cayman Islands","b":"1F1F0-1F1FE","j":["flag","cayman","islands","nation","country","banner","cayman_islands"]},"flag-kazakhstan":{"a":"Flag: Kazakhstan","b":"1F1F0-1F1FF","j":["flag","kz","nation","country","banner","kazakhstan"]},"flag-laos":{"a":"Flag: Laos","b":"1F1F1-1F1E6","j":["flag","lao","democratic","republic","nation","country","banner","laos"]},"flag-lebanon":{"a":"Flag: Lebanon","b":"1F1F1-1F1E7","j":["flag","lb","nation","country","banner","lebanon"]},"flag-st-lucia":{"a":"Flag: St. Lucia","b":"1F1F1-1F1E8","j":["flag","saint","lucia","nation","country","banner","st_lucia"]},"flag-liechtenstein":{"a":"Flag: Liechtenstein","b":"1F1F1-1F1EE","j":["flag","li","nation","country","banner","liechtenstein"]},"flag-sri-lanka":{"a":"Flag: Sri Lanka","b":"1F1F1-1F1F0","j":["flag","sri","lanka","nation","country","banner","sri_lanka"]},"flag-liberia":{"a":"Flag: Liberia","b":"1F1F1-1F1F7","j":["flag","lr","nation","country","banner","liberia"]},"flag-lesotho":{"a":"Flag: Lesotho","b":"1F1F1-1F1F8","j":["flag","ls","nation","country","banner","lesotho"]},"flag-lithuania":{"a":"Flag: Lithuania","b":"1F1F1-1F1F9","j":["flag","lt","nation","country","banner","lithuania"]},"flag-luxembourg":{"a":"Flag: Luxembourg","b":"1F1F1-1F1FA","j":["flag","lu","nation","country","banner","luxembourg"]},"flag-latvia":{"a":"Flag: Latvia","b":"1F1F1-1F1FB","j":["flag","lv","nation","country","banner","latvia"]},"flag-libya":{"a":"Flag: Libya","b":"1F1F1-1F1FE","j":["flag","ly","nation","country","banner","libya"]},"flag-morocco":{"a":"Flag: Morocco","b":"1F1F2-1F1E6","j":["flag","ma","nation","country","banner","morocco"]},"flag-monaco":{"a":"Flag: Monaco","b":"1F1F2-1F1E8","j":["flag","mc","nation","country","banner","monaco"]},"flag-moldova":{"a":"Flag: Moldova","b":"1F1F2-1F1E9","j":["flag","moldova","republic","nation","country","banner"]},"flag-montenegro":{"a":"Flag: Montenegro","b":"1F1F2-1F1EA","j":["flag","me","nation","country","banner","montenegro"]},"flag-st-martin":{"a":"Flag: St. Martin","b":"1F1F2-1F1EB","j":["flag"]},"flag-madagascar":{"a":"Flag: Madagascar","b":"1F1F2-1F1EC","j":["flag","mg","nation","country","banner","madagascar"]},"flag-marshall-islands":{"a":"Flag: Marshall Islands","b":"1F1F2-1F1ED","j":["flag","marshall","islands","nation","country","banner","marshall_islands"]},"flag-north-macedonia":{"a":"Flag: North Macedonia","b":"1F1F2-1F1F0","j":["flag","macedonia","nation","country","banner","north_macedonia"]},"flag-mali":{"a":"Flag: Mali","b":"1F1F2-1F1F1","j":["flag","ml","nation","country","banner","mali"]},"flag-myanmar-burma":{"a":"Flag: Myanmar (Burma)","b":"1F1F2-1F1F2","j":["flag","flag_myanmar","mm","nation","country","banner","myanmar"]},"flag-mongolia":{"a":"Flag: Mongolia","b":"1F1F2-1F1F3","j":["flag","mn","nation","country","banner","mongolia"]},"flag-macao-sar-china":{"a":"Flag: Macao Sar China","b":"1F1F2-1F1F4","j":["flag","macao","nation","country","banner","macao_sar_china"]},"flag-northern-mariana-islands":{"a":"Flag: Northern Mariana Islands","b":"1F1F2-1F1F5","j":["flag","northern","mariana","islands","nation","country","banner","northern_mariana_islands"]},"flag-martinique":{"a":"Flag: Martinique","b":"1F1F2-1F1F6","j":["flag","mq","nation","country","banner","martinique"]},"flag-mauritania":{"a":"Flag: Mauritania","b":"1F1F2-1F1F7","j":["flag","mr","nation","country","banner","mauritania"]},"flag-montserrat":{"a":"Flag: Montserrat","b":"1F1F2-1F1F8","j":["flag","ms","nation","country","banner","montserrat"]},"flag-malta":{"a":"Flag: Malta","b":"1F1F2-1F1F9","j":["flag","mt","nation","country","banner","malta"]},"flag-mauritius":{"a":"Flag: Mauritius","b":"1F1F2-1F1FA","j":["flag","mu","nation","country","banner","mauritius"]},"flag-maldives":{"a":"Flag: Maldives","b":"1F1F2-1F1FB","j":["flag","mv","nation","country","banner","maldives"]},"flag-malawi":{"a":"Flag: Malawi","b":"1F1F2-1F1FC","j":["flag","mw","nation","country","banner","malawi"]},"flag-mexico":{"a":"Flag: Mexico","b":"1F1F2-1F1FD","j":["flag","mx","nation","country","banner","mexico"]},"flag-malaysia":{"a":"Flag: Malaysia","b":"1F1F2-1F1FE","j":["flag","my","nation","country","banner","malaysia"]},"flag-mozambique":{"a":"Flag: Mozambique","b":"1F1F2-1F1FF","j":["flag","mz","nation","country","banner","mozambique"]},"flag-namibia":{"a":"Flag: Namibia","b":"1F1F3-1F1E6","j":["flag","na","nation","country","banner","namibia"]},"flag-new-caledonia":{"a":"Flag: New Caledonia","b":"1F1F3-1F1E8","j":["flag","new","caledonia","nation","country","banner","new_caledonia"]},"flag-niger":{"a":"Flag: Niger","b":"1F1F3-1F1EA","j":["flag","ne","nation","country","banner","niger"]},"flag-norfolk-island":{"a":"Flag: Norfolk Island","b":"1F1F3-1F1EB","j":["flag","norfolk","island","nation","country","banner","norfolk_island"]},"flag-nigeria":{"a":"Flag: Nigeria","b":"1F1F3-1F1EC","j":["flag","nation","country","banner","nigeria"]},"flag-nicaragua":{"a":"Flag: Nicaragua","b":"1F1F3-1F1EE","j":["flag","ni","nation","country","banner","nicaragua"]},"flag-netherlands":{"a":"Flag: Netherlands","b":"1F1F3-1F1F1","j":["flag","nl","nation","country","banner","netherlands"]},"flag-norway":{"a":"Flag: Norway","b":"1F1F3-1F1F4","j":["flag","no","nation","country","banner","norway"]},"flag-nepal":{"a":"Flag: Nepal","b":"1F1F3-1F1F5","j":["flag","np","nation","country","banner","nepal"]},"flag-nauru":{"a":"Flag: Nauru","b":"1F1F3-1F1F7","j":["flag","nr","nation","country","banner","nauru"]},"flag-niue":{"a":"Flag: Niue","b":"1F1F3-1F1FA","j":["flag","nu","nation","country","banner","niue"]},"flag-new-zealand":{"a":"Flag: New Zealand","b":"1F1F3-1F1FF","j":["flag","new","zealand","nation","country","banner","new_zealand"]},"flag-oman":{"a":"Flag: Oman","b":"1F1F4-1F1F2","j":["flag","om_symbol","nation","country","banner","oman"]},"flag-panama":{"a":"Flag: Panama","b":"1F1F5-1F1E6","j":["flag","pa","nation","country","banner","panama"]},"flag-peru":{"a":"Flag: Peru","b":"1F1F5-1F1EA","j":["flag","pe","nation","country","banner","peru"]},"flag-french-polynesia":{"a":"Flag: French Polynesia","b":"1F1F5-1F1EB","j":["flag","french","polynesia","nation","country","banner","french_polynesia"]},"flag-papua-new-guinea":{"a":"Flag: Papua New Guinea","b":"1F1F5-1F1EC","j":["flag","papua","new","guinea","nation","country","banner","papua_new_guinea"]},"flag-philippines":{"a":"Flag: Philippines","b":"1F1F5-1F1ED","j":["flag","ph","nation","country","banner","philippines"]},"flag-pakistan":{"a":"Flag: Pakistan","b":"1F1F5-1F1F0","j":["flag","pk","nation","country","banner","pakistan"]},"flag-poland":{"a":"Flag: Poland","b":"1F1F5-1F1F1","j":["flag","pl","nation","country","banner","poland"]},"flag-st-pierre--miquelon":{"a":"Flag: St. Pierre & Miquelon","b":"1F1F5-1F1F2","j":["flag","flag_st_pierre_miquelon","saint","pierre","miquelon","nation","country","banner","st_pierre_miquelon"]},"flag-pitcairn-islands":{"a":"Flag: Pitcairn Islands","b":"1F1F5-1F1F3","j":["flag","pitcairn","nation","country","banner","pitcairn_islands"]},"flag-puerto-rico":{"a":"Flag: Puerto Rico","b":"1F1F5-1F1F7","j":["flag","puerto","rico","nation","country","banner","puerto_rico"]},"flag-palestinian-territories":{"a":"Flag: Palestinian Territories","b":"1F1F5-1F1F8","j":["flag","palestine","palestinian","territories","nation","country","banner","palestinian_territories"]},"flag-portugal":{"a":"Flag: Portugal","b":"1F1F5-1F1F9","j":["flag","pt","nation","country","banner","portugal"]},"flag-palau":{"a":"Flag: Palau","b":"1F1F5-1F1FC","j":["flag","pw","nation","country","banner","palau"]},"flag-paraguay":{"a":"Flag: Paraguay","b":"1F1F5-1F1FE","j":["flag","py","nation","country","banner","paraguay"]},"flag-qatar":{"a":"Flag: Qatar","b":"1F1F6-1F1E6","j":["flag","qa","nation","country","banner","qatar"]},"flag-runion":{"a":"Flag: Réunion","b":"1F1F7-1F1EA","j":["flag","flag_reunion","réunion","nation","country","banner","reunion"]},"flag-romania":{"a":"Flag: Romania","b":"1F1F7-1F1F4","j":["flag","ro","nation","country","banner","romania"]},"flag-serbia":{"a":"Flag: Serbia","b":"1F1F7-1F1F8","j":["flag","rs","nation","country","banner","serbia"]},"flag-russia":{"a":"Flag: Russia","b":"1F1F7-1F1FA","j":["flag","russian","federation","nation","country","banner","russia"]},"flag-rwanda":{"a":"Flag: Rwanda","b":"1F1F7-1F1FC","j":["flag","rw","nation","country","banner","rwanda"]},"flag-saudi-arabia":{"a":"Flag: Saudi Arabia","b":"1F1F8-1F1E6","j":["flag","nation","country","banner","saudi_arabia"]},"flag-solomon-islands":{"a":"Flag: Solomon Islands","b":"1F1F8-1F1E7","j":["flag","solomon","islands","nation","country","banner","solomon_islands"]},"flag-seychelles":{"a":"Flag: Seychelles","b":"1F1F8-1F1E8","j":["flag","sc","nation","country","banner","seychelles"]},"flag-sudan":{"a":"Flag: Sudan","b":"1F1F8-1F1E9","j":["flag","sd","nation","country","banner","sudan"]},"flag-sweden":{"a":"Flag: Sweden","b":"1F1F8-1F1EA","j":["flag","se","nation","country","banner","sweden"]},"flag-singapore":{"a":"Flag: Singapore","b":"1F1F8-1F1EC","j":["flag","sg","nation","country","banner","singapore"]},"flag-st-helena":{"a":"Flag: St. Helena","b":"1F1F8-1F1ED","j":["flag","saint","helena","ascension","tristan","cunha","nation","country","banner","st_helena"]},"flag-slovenia":{"a":"Flag: Slovenia","b":"1F1F8-1F1EE","j":["flag","si","nation","country","banner","slovenia"]},"flag-svalbard--jan-mayen":{"a":"Flag: Svalbard & Jan Mayen","b":"1F1F8-1F1EF","j":["flag","flag_svalbard_jan_mayen"]},"flag-slovakia":{"a":"Flag: Slovakia","b":"1F1F8-1F1F0","j":["flag","sk","nation","country","banner","slovakia"]},"flag-sierra-leone":{"a":"Flag: Sierra Leone","b":"1F1F8-1F1F1","j":["flag","sierra","leone","nation","country","banner","sierra_leone"]},"flag-san-marino":{"a":"Flag: San Marino","b":"1F1F8-1F1F2","j":["flag","san","marino","nation","country","banner","san_marino"]},"flag-senegal":{"a":"Flag: Senegal","b":"1F1F8-1F1F3","j":["flag","sn","nation","country","banner","senegal"]},"flag-somalia":{"a":"Flag: Somalia","b":"1F1F8-1F1F4","j":["flag","so","nation","country","banner","somalia"]},"flag-suriname":{"a":"Flag: Suriname","b":"1F1F8-1F1F7","j":["flag","sr","nation","country","banner","suriname"]},"flag-south-sudan":{"a":"Flag: South Sudan","b":"1F1F8-1F1F8","j":["flag","south","sd","nation","country","banner","south_sudan"]},"flag-so-tom--prncipe":{"a":"Flag: São Tomé & Príncipe","b":"1F1F8-1F1F9","j":["flag","flag_sao_tome_principe","sao","tome","principe","nation","country","banner","sao_tome_principe"]},"flag-el-salvador":{"a":"Flag: El Salvador","b":"1F1F8-1F1FB","j":["flag","el","salvador","nation","country","banner","el_salvador"]},"flag-sint-maarten":{"a":"Flag: Sint Maarten","b":"1F1F8-1F1FD","j":["flag","sint","maarten","dutch","nation","country","banner","sint_maarten"]},"flag-syria":{"a":"Flag: Syria","b":"1F1F8-1F1FE","j":["flag","syrian","arab","republic","nation","country","banner","syria"]},"flag-eswatini":{"a":"Flag: Eswatini","b":"1F1F8-1F1FF","j":["flag","sz","nation","country","banner","eswatini"]},"flag-tristan-da-cunha":{"a":"Flag: Tristan Da Cunha","b":"1F1F9-1F1E6","j":["flag"]},"flag-turks--caicos-islands":{"a":"Flag: Turks & Caicos Islands","b":"1F1F9-1F1E8","j":["flag","flag_turks_caicos_islands","turks","caicos","islands","nation","country","banner","turks_caicos_islands"]},"flag-chad":{"a":"Flag: Chad","b":"1F1F9-1F1E9","j":["flag","td","nation","country","banner","chad"]},"flag-french-southern-territories":{"a":"Flag: French Southern Territories","b":"1F1F9-1F1EB","j":["flag","french","southern","territories","nation","country","banner","french_southern_territories"]},"flag-togo":{"a":"Flag: Togo","b":"1F1F9-1F1EC","j":["flag","tg","nation","country","banner","togo"]},"flag-thailand":{"a":"Flag: Thailand","b":"1F1F9-1F1ED","j":["flag","th","nation","country","banner","thailand"]},"flag-tajikistan":{"a":"Flag: Tajikistan","b":"1F1F9-1F1EF","j":["flag","tj","nation","country","banner","tajikistan"]},"flag-tokelau":{"a":"Flag: Tokelau","b":"1F1F9-1F1F0","j":["flag","tk","nation","country","banner","tokelau"]},"flag-timorleste":{"a":"Flag: Timor-Leste","b":"1F1F9-1F1F1","j":["flag","flag_timor_leste","timor","leste","nation","country","banner","timor_leste"]},"flag-turkmenistan":{"a":"Flag: Turkmenistan","b":"1F1F9-1F1F2","j":["flag","nation","country","banner","turkmenistan"]},"flag-tunisia":{"a":"Flag: Tunisia","b":"1F1F9-1F1F3","j":["flag","tn","nation","country","banner","tunisia"]},"flag-tonga":{"a":"Flag: Tonga","b":"1F1F9-1F1F4","j":["flag","to","nation","country","banner","tonga"]},"flag-turkey":{"a":"Flag: Turkey","b":"1F1F9-1F1F7","j":["flag","turkey","nation","country","banner"]},"flag-trinidad--tobago":{"a":"Flag: Trinidad & Tobago","b":"1F1F9-1F1F9","j":["flag","flag_trinidad_tobago","trinidad","tobago","nation","country","banner","trinidad_tobago"]},"flag-tuvalu":{"a":"Flag: Tuvalu","b":"1F1F9-1F1FB","j":["flag","nation","country","banner","tuvalu"]},"flag-taiwan":{"a":"Flag: Taiwan","b":"1F1F9-1F1FC","j":["flag","tw","nation","country","banner","taiwan"]},"flag-tanzania":{"a":"Flag: Tanzania","b":"1F1F9-1F1FF","j":["flag","tanzania","united","republic","nation","country","banner"]},"flag-ukraine":{"a":"Flag: Ukraine","b":"1F1FA-1F1E6","j":["flag","ua","nation","country","banner","ukraine"]},"flag-uganda":{"a":"Flag: Uganda","b":"1F1FA-1F1EC","j":["flag","ug","nation","country","banner","uganda"]},"flag-us-outlying-islands":{"a":"Flag: U.S. Outlying Islands","b":"1F1FA-1F1F2","j":["flag","flag_u_s_outlying_islands"]},"flag-united-nations":{"a":"Flag: United Nations","b":"1F1FA-1F1F3","j":["flag","un","banner"]},"flag-united-states":{"a":"Flag: United States","b":"1F1FA-1F1F8","j":["flag","united","states","america","nation","country","banner","united_states"]},"flag-uruguay":{"a":"Flag: Uruguay","b":"1F1FA-1F1FE","j":["flag","uy","nation","country","banner","uruguay"]},"flag-uzbekistan":{"a":"Flag: Uzbekistan","b":"1F1FA-1F1FF","j":["flag","uz","nation","country","banner","uzbekistan"]},"flag-vatican-city":{"a":"Flag: Vatican City","b":"1F1FB-1F1E6","j":["flag","vatican","city","nation","country","banner","vatican_city"]},"flag-st-vincent--grenadines":{"a":"Flag: St. Vincent & Grenadines","b":"1F1FB-1F1E8","j":["flag","flag_st_vincent_grenadines","saint","vincent","grenadines","nation","country","banner","st_vincent_grenadines"]},"flag-venezuela":{"a":"Flag: Venezuela","b":"1F1FB-1F1EA","j":["flag","ve","bolivarian","republic","nation","country","banner","venezuela"]},"flag-british-virgin-islands":{"a":"Flag: British Virgin Islands","b":"1F1FB-1F1EC","j":["flag","british","virgin","islands","bvi","nation","country","banner","british_virgin_islands"]},"flag-us-virgin-islands":{"a":"Flag: U.S. Virgin Islands","b":"1F1FB-1F1EE","j":["flag","flag_u_s_virgin_islands","virgin","islands","us","nation","country","banner","u_s_virgin_islands"]},"flag-vietnam":{"a":"Flag: Vietnam","b":"1F1FB-1F1F3","j":["flag","viet","nam","nation","country","banner","vietnam"]},"flag-vanuatu":{"a":"Flag: Vanuatu","b":"1F1FB-1F1FA","j":["flag","vu","nation","country","banner","vanuatu"]},"flag-wallis--futuna":{"a":"Flag: Wallis & Futuna","b":"1F1FC-1F1EB","j":["flag","flag_wallis_futuna","wallis","futuna","nation","country","banner","wallis_futuna"]},"flag-samoa":{"a":"Flag: Samoa","b":"1F1FC-1F1F8","j":["flag","ws","nation","country","banner","samoa"]},"flag-kosovo":{"a":"Flag: Kosovo","b":"1F1FD-1F1F0","j":["flag","xk","nation","country","banner","kosovo"]},"flag-yemen":{"a":"Flag: Yemen","b":"1F1FE-1F1EA","j":["flag","ye","nation","country","banner","yemen"]},"flag-mayotte":{"a":"Flag: Mayotte","b":"1F1FE-1F1F9","j":["flag","yt","nation","country","banner","mayotte"]},"flag-south-africa":{"a":"Flag: South Africa","b":"1F1FF-1F1E6","j":["flag","south","africa","nation","country","banner","south_africa"]},"flag-zambia":{"a":"Flag: Zambia","b":"1F1FF-1F1F2","j":["flag","zm","nation","country","banner","zambia"]},"flag-zimbabwe":{"a":"Flag: Zimbabwe","b":"1F1FF-1F1FC","j":["flag","zw","nation","country","banner","zimbabwe"]},"flag-england":{"a":"Flag: England","b":"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F","j":["flag","english"]},"flag-scotland":{"a":"Flag: Scotland","b":"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F","j":["flag","scottish"]},"flag-wales":{"a":"Flag: Wales","b":"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F","j":["flag","welsh"]}},"aliases":{}}
\ No newline at end of file
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index f61d5fe7bc..9fac6d722a 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -47,8 +47,8 @@
@@ -89,4 +89,11 @@
android:summary="@string/labs_enable_new_app_layout_summary"
android:title="@string/labs_enable_new_app_layout_title" />
+
+
diff --git a/vector/src/main/res/xml/vector_settings_security_privacy.xml b/vector/src/main/res/xml/vector_settings_security_privacy.xml
index c246a40f71..1e8997e9c8 100644
--- a/vector/src/main/res/xml/vector_settings_security_privacy.xml
+++ b/vector/src/main/res/xml/vector_settings_security_privacy.xml
@@ -35,7 +35,7 @@
()
+ every { ClipData.newPlainText(any(), any()) } returns clipData
+
+ // When
+ copyToClipboardUseCase.execute(A_TEXT)
+
+ // Then
+ clipboardManager.verifySetPrimaryClip(clipData)
+ verify { ClipData.newPlainText("", A_TEXT) }
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt
index cc5cdf6e39..351d6b8eb0 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt
@@ -52,8 +52,8 @@ class DevicesViewModelTest {
fakeActiveSessionHolder.instance,
getCurrentSessionCrossSigningInfoUseCase,
getDeviceFullInfoListUseCase,
- refreshDevicesUseCase,
refreshDevicesOnCryptoDevicesChangeUseCase,
+ refreshDevicesUseCase,
)
}
@@ -181,7 +181,7 @@ class DevicesViewModelTest {
)
val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2)
val deviceFullInfoListFlow = flowOf(deviceFullInfoList)
- every { getDeviceFullInfoListUseCase.execute() } returns deviceFullInfoListFlow
+ every { getDeviceFullInfoListUseCase.execute(any(), any()) } returns deviceFullInfoListFlow
return deviceFullInfoList
}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
index fa9f742976..54b160f196 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
@@ -16,6 +16,8 @@
package im.vector.app.features.settings.devices.v2
+import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.test
@@ -47,12 +49,14 @@ class GetDeviceFullInfoListUseCaseTest {
private val checkIfSessionIsInactiveUseCase = mockk()
private val getEncryptionTrustLevelForDeviceUseCase = mockk()
private val getCurrentSessionCrossSigningInfoUseCase = mockk()
+ private val filterDevicesUseCase = mockk()
private val getDeviceFullInfoListUseCase = GetDeviceFullInfoListUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
checkIfSessionIsInactiveUseCase = checkIfSessionIsInactiveUseCase,
getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase,
getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase,
+ filterDevicesUseCase = filterDevicesUseCase,
)
@Before
@@ -117,9 +121,10 @@ class GetDeviceFullInfoListUseCaseTest {
isInactive = false
)
val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
+ every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult
// When
- val result = getDeviceFullInfoListUseCase.execute()
+ val result = getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, excludeCurrentDevice = false)
.test(this)
// Then
@@ -144,7 +149,7 @@ class GetDeviceFullInfoListUseCaseTest {
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
// When
- val result = getDeviceFullInfoListUseCase.execute()
+ val result = getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, excludeCurrentDevice = false)
.test(this)
// Then
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt
index 2a4c53f34f..a1f0918b31 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt
@@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2
import android.content.Intent
+import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
import im.vector.app.test.fakes.FakeContext
import io.mockk.every
@@ -38,6 +39,7 @@ class VectorSettingsDevicesViewNavigatorTest {
@Before
fun setUp() {
mockkObject(SessionOverviewActivity.Companion)
+ mockkObject(OtherSessionsActivity.Companion)
}
@After
@@ -57,9 +59,27 @@ class VectorSettingsDevicesViewNavigatorTest {
}
}
+ @Test
+ fun `given an intent when navigating to other sessions list then it starts the correct activity`() {
+ val intent = givenIntentForOtherSessions()
+ context.givenStartActivity(intent)
+
+ vectorSettingsDevicesViewNavigator.navigateToOtherSessions(context.instance)
+
+ verify {
+ context.instance.startActivity(intent)
+ }
+ }
+
private fun givenIntentForSessionOverview(sessionId: String): Intent {
val intent = mockk()
every { SessionOverviewActivity.newIntent(context.instance, sessionId) } returns intent
return intent
}
+
+ private fun givenIntentForOtherSessions(): Intent {
+ val intent = mockk()
+ every { OtherSessionsActivity.newIntent(context.instance) } returns intent
+ return intent
+ }
}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt
new file mode 100644
index 0000000000..b618c58b7e
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import io.mockk.every
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+
+private const val AN_IP_ADDRESS = "ip-address"
+
+class CheckIfSectionDeviceIsVisibleUseCaseTest {
+
+ private val checkIfSectionDeviceIsVisibleUseCase = CheckIfSectionDeviceIsVisibleUseCase()
+
+ @Test
+ fun `given device info with Ip address when checking is device section is visible then it returns true`() = runTest {
+ // Given
+ val deviceInfo = givenADeviceInfo(AN_IP_ADDRESS)
+
+ // When
+ val result = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo)
+
+ // Then
+ result shouldBeEqualTo true
+ }
+
+ @Test
+ fun `given device info with empty or null Ip address when checking is device section is visible then it returns false`() = runTest {
+ // Given
+ val deviceInfo1 = givenADeviceInfo("")
+ val deviceInfo2 = givenADeviceInfo(null)
+
+ // When
+ val result1 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo1)
+ val result2 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo2)
+
+ // Then
+ result1 shouldBeEqualTo false
+ result2 shouldBeEqualTo false
+ }
+
+ private fun givenADeviceInfo(ipAddress: String?): DeviceInfo {
+ val info = mockk()
+ every { info.lastSeenIp } returns ipAddress
+ return info
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt
new file mode 100644
index 0000000000..806c86d175
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import io.mockk.every
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+
+private const val A_SESSION_NAME = "session-name"
+private const val A_SESSION_ID = "session-id"
+private const val A_LAST_SEEN_TS = 123L
+
+class CheckIfSectionSessionIsVisibleUseCaseTest {
+
+ private val checkIfSectionSessionIsVisibleUseCase = CheckIfSectionSessionIsVisibleUseCase()
+
+ @Test
+ fun `given device info with name, id or lastSeenTs when checking is session section is visible then it returns true`() = runTest {
+ // Given
+ val deviceInfoList = listOf(
+ givenADeviceInfo(
+ sessionName = A_SESSION_NAME,
+ sessionId = null,
+ lastSeenTs = null,
+ ),
+ givenADeviceInfo(
+ sessionName = null,
+ sessionId = A_SESSION_ID,
+ lastSeenTs = null,
+ ),
+ givenADeviceInfo(
+ sessionName = null,
+ sessionId = null,
+ lastSeenTs = A_LAST_SEEN_TS,
+ ),
+ givenADeviceInfo(
+ sessionName = A_SESSION_NAME,
+ sessionId = A_SESSION_ID,
+ lastSeenTs = null,
+ ),
+ givenADeviceInfo(
+ sessionName = A_SESSION_NAME,
+ sessionId = null,
+ lastSeenTs = A_LAST_SEEN_TS,
+ ),
+ givenADeviceInfo(
+ sessionName = null,
+ sessionId = A_SESSION_ID,
+ lastSeenTs = A_LAST_SEEN_TS,
+ ),
+ givenADeviceInfo(
+ sessionName = A_SESSION_NAME,
+ sessionId = A_SESSION_ID,
+ lastSeenTs = A_LAST_SEEN_TS,
+ ),
+ )
+
+ deviceInfoList.forEach { deviceInfo ->
+ // When
+ val result = checkIfSectionSessionIsVisibleUseCase.execute(deviceInfo)
+
+ // Then
+ result shouldBeEqualTo true
+ }
+ }
+
+ @Test
+ fun `given device info with missing session info when checking is session section is visible then it returns true`() = runTest {
+ // Given
+ val deviceInfoList = listOf(
+ givenADeviceInfo(
+ sessionName = null,
+ sessionId = null,
+ lastSeenTs = null,
+ ),
+ givenADeviceInfo(
+ sessionName = "",
+ sessionId = "",
+ lastSeenTs = null,
+ ),
+ givenADeviceInfo(
+ sessionName = "",
+ sessionId = "",
+ lastSeenTs = -1,
+ ),
+ )
+
+ deviceInfoList.forEach { deviceInfo ->
+ // When
+ val result = checkIfSectionSessionIsVisibleUseCase.execute(deviceInfo)
+
+ // Then
+ result shouldBeEqualTo false
+ }
+ }
+
+ private fun givenADeviceInfo(
+ sessionName: String?,
+ sessionId: String?,
+ lastSeenTs: Long?,
+ ): DeviceInfo {
+ val info = mockk()
+ every { info.displayName } returns sessionName
+ every { info.deviceId } returns sessionId
+ every { info.lastSeenTs } returns lastSeenTs
+ return info
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt
new file mode 100644
index 0000000000..df0613e06b
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.details
+
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.test.MvRxTestRule
+import im.vector.app.core.utils.CopyToClipboardUseCase
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import im.vector.app.features.settings.devices.v2.overview.GetDeviceFullInfoUseCase
+import im.vector.app.test.test
+import im.vector.app.test.testDispatcher
+import io.mockk.every
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.runs
+import io.mockk.verify
+import kotlinx.coroutines.flow.flowOf
+import org.junit.Rule
+import org.junit.Test
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+
+private const val A_SESSION_ID = "session-id"
+private const val A_TEXT = "text"
+
+class SessionDetailsViewModelTest {
+
+ @get:Rule
+ val mvRxTestRule = MvRxTestRule(testDispatcher = testDispatcher)
+
+ private val args = SessionDetailsArgs(
+ deviceId = A_SESSION_ID
+ )
+ private val getDeviceFullInfoUseCase = mockk()
+ private val copyToClipboardUseCase = mockk()
+
+ private fun createViewModel() = SessionDetailsViewModel(
+ initialState = SessionDetailsViewState(args),
+ getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
+ copyToClipboardUseCase = copyToClipboardUseCase,
+ )
+
+ @Test
+ fun `given the viewModel has been initialized then viewState is updated with session info`() {
+ // Given
+ val deviceFullInfo = mockk()
+ val deviceInfo = mockk()
+ every { deviceFullInfo.deviceInfo } returns deviceInfo
+ every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo)
+ val expectedState = SessionDetailsViewState(
+ deviceId = A_SESSION_ID,
+ deviceInfo = Success(deviceInfo)
+ )
+
+ // When
+ val viewModel = createViewModel()
+
+ // Then
+ viewModel.test()
+ .assertLatestState { state -> state == expectedState }
+ .finish()
+ verify { getDeviceFullInfoUseCase.execute(A_SESSION_ID) }
+ }
+
+ @Test
+ fun `given copyToClipboard action when viewModel handle it then related use case is executed and viewEvent is updated`() {
+ // Given
+ val deviceFullInfo = mockk()
+ val deviceInfo = mockk()
+ every { deviceFullInfo.deviceInfo } returns deviceInfo
+ every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo)
+ val action = SessionDetailsAction.CopyToClipboard(A_TEXT)
+ every { copyToClipboardUseCase.execute(any()) } just runs
+
+ // When
+ val viewModel = createViewModel()
+ val viewModelTest = viewModel.test()
+ viewModel.handle(action)
+
+ // Then
+ viewModelTest
+ .assertEvent { it is SessionDetailsViewEvent.ContentCopiedToClipboard }
+ .finish()
+ verify { copyToClipboardUseCase.execute(A_TEXT) }
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt
new file mode 100644
index 0000000000..1254e2a80a
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.filter
+
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldContainAll
+import org.junit.Test
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+
+private val activeVerifiedDevice = DeviceFullInfo(
+ deviceInfo = DeviceInfo(deviceId = "ACTIVE_VERIFIED_DEVICE"),
+ cryptoDeviceInfo = CryptoDeviceInfo(
+ userId = "USER_ID_1",
+ deviceId = "ACTIVE_VERIFIED_DEVICE",
+ trustLevel = DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
+ ),
+ roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
+ isInactive = false
+)
+private val inactiveVerifiedDevice = DeviceFullInfo(
+ deviceInfo = DeviceInfo(deviceId = "INACTIVE_VERIFIED_DEVICE"),
+ cryptoDeviceInfo = CryptoDeviceInfo(
+ userId = "USER_ID_1",
+ deviceId = "INACTIVE_VERIFIED_DEVICE",
+ trustLevel = DeviceTrustLevel(crossSigningVerified = true, locallyVerified = true)
+ ),
+ roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
+ isInactive = true
+)
+private val activeUnverifiedDevice = DeviceFullInfo(
+ deviceInfo = DeviceInfo(deviceId = "ACTIVE_UNVERIFIED_DEVICE"),
+ cryptoDeviceInfo = CryptoDeviceInfo(
+ userId = "USER_ID_1",
+ deviceId = "ACTIVE_UNVERIFIED_DEVICE",
+ trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false)
+ ),
+ roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
+ isInactive = false
+)
+private val inactiveUnverifiedDevice = DeviceFullInfo(
+ deviceInfo = DeviceInfo(deviceId = "INACTIVE_UNVERIFIED_DEVICE"),
+ cryptoDeviceInfo = CryptoDeviceInfo(
+ userId = "USER_ID_1",
+ deviceId = "INACTIVE_UNVERIFIED_DEVICE",
+ trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false)
+ ),
+ roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
+ isInactive = true
+)
+
+private val devices = listOf(
+ activeVerifiedDevice,
+ inactiveVerifiedDevice,
+ activeUnverifiedDevice,
+ inactiveUnverifiedDevice,
+)
+
+class FilterDevicesUseCaseTest {
+
+ private val filterDevicesUseCase = FilterDevicesUseCase()
+
+ @Test
+ fun `given a device list when filter type is ALL_SESSIONS then returns the same list`() {
+ val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList())
+
+ filteredDeviceList.size shouldBeEqualTo devices.size
+ }
+
+ @Test
+ fun `given a device list when filter type is VERIFIED then returns only verified devices`() {
+ val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.VERIFIED, emptyList())
+
+ filteredDeviceList.size shouldBeEqualTo 2
+ filteredDeviceList shouldContainAll listOf(activeVerifiedDevice, inactiveVerifiedDevice)
+ }
+
+ @Test
+ fun `given a device list when filter type is UNVERIFIED then returns only unverified devices`() {
+ val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
+
+ filteredDeviceList.size shouldBeEqualTo 2
+ filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice)
+ }
+
+ @Test
+ fun `given a device list when filter type is INACTIVE then returns only inactive devices`() {
+ val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.INACTIVE, emptyList())
+
+ filteredDeviceList.size shouldBeEqualTo 2
+ filteredDeviceList shouldContainAll listOf(inactiveVerifiedDevice, inactiveUnverifiedDevice)
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt
new file mode 100644
index 0000000000..3123572521
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.othersessions
+
+import android.content.Intent
+import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity
+import im.vector.app.test.fakes.FakeContext
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
+import io.mockk.verify
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private const val A_DEVICE_ID = "A_DEVICE_ID"
+
+class OtherSessionsViewNavigatorTest {
+
+ private val context = FakeContext()
+ private val otherSessionsViewNavigator = OtherSessionsViewNavigator()
+
+ @Before
+ fun setUp() {
+ mockkObject(SessionOverviewActivity)
+ }
+
+ @After
+ fun tearDown() {
+ unmockkAll()
+ }
+
+ @Test
+ fun `given a device id when navigating to overview then it starts the correct activity`() {
+ val intent = givenIntentForDeviceOverview(A_DEVICE_ID)
+ context.givenStartActivity(intent)
+
+ otherSessionsViewNavigator.navigateToSessionOverview(context.instance, A_DEVICE_ID)
+
+ verify {
+ context.instance.startActivity(intent)
+ }
+ }
+
+ private fun givenIntentForDeviceOverview(deviceId: String): Intent {
+ val intent = mockk()
+ every { SessionOverviewActivity.newIntent(context.instance, deviceId) } returns intent
+ return intent
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt
index 7dc8e08a4e..9c7515f2da 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt
@@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldBeNull
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -72,6 +73,7 @@ class GetDeviceFullInfoUseCaseTest {
@Test
fun `given current session and info for device when getting device info then the result is correct`() = runTest {
+ // Given
val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo()
val deviceInfo = DeviceInfo(
lastSeenTs = A_TIMESTAMP
@@ -85,15 +87,15 @@ class GetDeviceFullInfoUseCaseTest {
val isInactive = false
every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive
+ // When
val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull()
- deviceFullInfo shouldBeEqualTo Optional(
- DeviceFullInfo(
- deviceInfo = deviceInfo,
- cryptoDeviceInfo = cryptoDeviceInfo,
- roomEncryptionTrustLevel = trustLevel,
- isInactive = isInactive,
- )
+ // Then
+ deviceFullInfo shouldBeEqualTo DeviceFullInfo(
+ deviceInfo = deviceInfo,
+ cryptoDeviceInfo = cryptoDeviceInfo,
+ roomEncryptionTrustLevel = trustLevel,
+ isInactive = isInactive,
)
verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
verify { getCurrentSessionCrossSigningInfoUseCase.execute() }
@@ -104,16 +106,19 @@ class GetDeviceFullInfoUseCaseTest {
}
@Test
- fun `given current session and no info for device when getting device info then the result is null`() = runTest {
+ fun `given current session and no info for device when getting device info then the result is empty`() = runTest {
+ // Given
givenCurrentSessionCrossSigningInfo()
fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData = MutableLiveData(Optional(null))
fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData.givenAsFlow()
fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData = MutableLiveData(Optional(null))
fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData.givenAsFlow()
+ // When
val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull()
- deviceFullInfo shouldBeEqualTo Optional(null)
+ // Then
+ deviceFullInfo.shouldBeNull()
verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow() }
verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow() }
@@ -121,11 +126,14 @@ class GetDeviceFullInfoUseCaseTest {
@Test
fun `given no current session when getting device info then the result is empty`() = runTest {
+ // Given
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
+ // When
val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull()
- deviceFullInfo shouldBeEqualTo null
+ // Then
+ deviceFullInfo.shouldBeNull()
verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
index 4a26fc4adc..8d4e49ef85 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
@@ -21,22 +21,21 @@ import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.test
+import im.vector.app.test.testDispatcher
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Rule
import org.junit.Test
import org.matrix.android.sdk.api.auth.data.SessionParams
-import org.matrix.android.sdk.api.util.Optional
private const val A_SESSION_ID = "session-id"
class SessionOverviewViewModelTest {
@get:Rule
- val mvRxTestRule = MvRxTestRule(testDispatcher = UnconfinedTestDispatcher())
+ val mvRxTestRule = MvRxTestRule(testDispatcher = testDispatcher)
private val args = SessionOverviewArgs(
deviceId = A_SESSION_ID
@@ -52,17 +51,20 @@ class SessionOverviewViewModelTest {
@Test
fun `given the viewModel has been initialized then viewState is updated with session info`() {
+ // Given
val sessionParams = givenIdForSession(A_SESSION_ID)
val deviceFullInfo = mockk()
- every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(Optional(deviceFullInfo))
+ every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo)
val expectedState = SessionOverviewViewState(
deviceId = A_SESSION_ID,
isCurrentSession = true,
deviceInfo = Success(deviceFullInfo)
)
+ // When
val viewModel = createViewModel()
+ // Then
viewModel.test()
.assertLatestState { state -> state == expectedState }
.finish()
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt
new file mode 100644
index 0000000000..56f1e5920d
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.features.settings.devices.v2.overview
+
+import android.content.Intent
+import im.vector.app.features.settings.devices.v2.details.SessionDetailsActivity
+import im.vector.app.test.fakes.FakeContext
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
+import io.mockk.verify
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private const val A_SESSION_ID = "session_id"
+
+class SessionOverviewViewNavigatorTest {
+
+ private val context = FakeContext()
+ private val sessionOverviewViewNavigator = SessionOverviewViewNavigator()
+
+ @Before
+ fun setUp() {
+ mockkObject(SessionDetailsActivity)
+ }
+
+ @After
+ fun tearDown() {
+ unmockkAll()
+ }
+
+ @Test
+ fun `given a session id when navigating to details then it starts the correct activity`() {
+ // Given
+ val intent = givenIntentForSessionDetails(A_SESSION_ID)
+ context.givenStartActivity(intent)
+
+ // When
+ sessionOverviewViewNavigator.navigateToSessionDetails(context.instance, A_SESSION_ID)
+
+ // Then
+ verify {
+ context.instance.startActivity(intent)
+ }
+ }
+
+ private fun givenIntentForSessionDetails(sessionId: String): Intent {
+ val intent = mockk()
+ every { SessionDetailsActivity.newIntent(context.instance, sessionId) } returns intent
+ return intent
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeClipboardManager.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeClipboardManager.kt
new file mode 100644
index 0000000000..983902b496
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeClipboardManager.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * 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 im.vector.app.test.fakes
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import io.mockk.every
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.runs
+import io.mockk.verify
+
+class FakeClipboardManager {
+ val instance = mockk()
+
+ fun givenSetPrimaryClip() {
+ every { instance.setPrimaryClip(any()) } just runs
+ }
+
+ fun verifySetPrimaryClip(clipData: ClipData) {
+ verify { instance.setPrimaryClip(clipData) }
+ }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt
index d74ebcb678..9a94313fec 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt
@@ -16,6 +16,7 @@
package im.vector.app.test.fakes
+import android.content.ClipboardManager
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
@@ -74,4 +75,10 @@ class FakeContext(
fun givenStartActivity(intent: Intent) {
every { instance.startActivity(intent) } just runs
}
+
+ fun givenClipboardManager(): FakeClipboardManager {
+ val fakeClipboardManager = FakeClipboardManager()
+ givenService(Context.CLIPBOARD_SERVICE, ClipboardManager::class.java, fakeClipboardManager.instance)
+ return fakeClipboardManager
+ }
}